From cdd13b1af36dfbe51f3876efab7eccf445232682 Mon Sep 17 00:00:00 2001 From: funshine Date: Mon, 22 Dec 2014 19:35:05 +0800 Subject: [PATCH] source file first commit, folder structure refact --- Makefile | 174 ++ README.md | 50 +- app/.gitignore | 2 + app/Makefile | 166 ++ app/driver/Makefile | 45 + app/driver/gpio16.c | 42 + app/driver/i2c_master.c | 293 +++ app/driver/key.c | 162 ++ app/driver/onewire.c | 547 ++++ app/driver/pwm.c | 446 ++++ app/driver/readline.c | 112 + app/driver/spi.c | 494 ++++ app/driver/uart.c | 250 ++ app/gen_misc.bat | 27 + app/gen_misc.sh | 25 + app/gen_misc_plus.bat | 27 + app/gen_misc_plus.sh | 28 + app/include/arch/cc.h | 117 + app/include/arch/perf.h | 40 + app/include/arch/sys_arch.h | 0 app/include/driver/gpio16.h | 9 + app/include/driver/i2c_master.h | 69 + app/include/driver/key.h | 27 + app/include/driver/onewire.h | 150 ++ app/include/driver/pwm.h | 46 + app/include/driver/spi.h | 48 + app/include/driver/spi_master.h | 13 + app/include/driver/spi_register.h | 182 ++ app/include/driver/uart.h | 102 + app/include/driver/uart_register.h | 128 + app/include/lwip/api.h | 284 ++ app/include/lwip/api_msg.h | 174 ++ app/include/lwip/app/dhcpserver.h | 89 + app/include/lwip/app/espconn.h | 398 +++ app/include/lwip/app/espconn_tcp.h | 43 + app/include/lwip/app/espconn_udp.h | 51 + app/include/lwip/app/ping.h | 85 + app/include/lwip/arch.h | 238 ++ app/include/lwip/autoip.h | 119 + app/include/lwip/debug.h | 98 + app/include/lwip/def.h | 127 + app/include/lwip/dhcp.h | 243 ++ app/include/lwip/dns.h | 124 + app/include/lwip/err.h | 86 + app/include/lwip/icmp.h | 115 + app/include/lwip/igmp.h | 106 + app/include/lwip/inet.h | 107 + app/include/lwip/inet_chksum.h | 90 + app/include/lwip/init.h | 73 + app/include/lwip/ip.h | 215 ++ app/include/lwip/ip_addr.h | 256 ++ app/include/lwip/ip_frag.h | 88 + app/include/lwip/mem.h | 143 + app/include/lwip/memp.h | 116 + app/include/lwip/memp_std.h | 126 + app/include/lwip/netbuf.h | 101 + app/include/lwip/netdb.h | 124 + app/include/lwip/netif.h | 315 +++ app/include/lwip/netifapi.h | 108 + app/include/lwip/opt.h | 2043 ++++++++++++++ app/include/lwip/pbuf.h | 160 ++ app/include/lwip/raw.h | 98 + app/include/lwip/sio.h | 141 + app/include/lwip/snmp.h | 367 +++ app/include/lwip/snmp_asn1.h | 101 + app/include/lwip/snmp_msg.h | 315 +++ app/include/lwip/snmp_structs.h | 268 ++ app/include/lwip/sockets.h | 376 +++ app/include/lwip/stats.h | 292 ++ app/include/lwip/sys.h | 337 +++ app/include/lwip/tcp.h | 377 +++ app/include/lwip/tcp_impl.h | 472 ++++ app/include/lwip/tcpip.h | 159 ++ app/include/lwip/timers.h | 98 + app/include/lwip/udp.h | 171 ++ app/include/lwipopts.h | 2052 +++++++++++++++ app/include/mem_manager.h | 81 + app/include/netif/etharp.h | 254 ++ app/include/netif/if_llc.h | 173 ++ app/include/netif/ppp_oe.h | 190 ++ app/include/netif/wlan_lwip_if.h | 25 + app/include/pp/esf_buf.h | 12 + app/include/ssl/app/espconn_secure.h | 46 + app/include/ssl/app/espconn_ssl.h | 59 + app/include/ssl/cert.h | 36 + app/include/ssl/private_key.h | 30 + app/include/ssl/ssl_bigint.h | 99 + app/include/ssl/ssl_bigint_impl.h | 131 + app/include/ssl/ssl_cert.h | 43 + app/include/ssl/ssl_config.h | 127 + app/include/ssl/ssl_crypto.h | 230 ++ app/include/ssl/ssl_crypto_misc.h | 172 ++ app/include/ssl/ssl_os_int.h | 67 + app/include/ssl/ssl_os_port.h | 74 + app/include/ssl/ssl_private_key.h | 54 + app/include/ssl/ssl_ssl.h | 503 ++++ app/include/ssl/ssl_tls1.h | 302 +++ app/include/ssl/ssl_version.h | 1 + app/include/user_config.h | 71 + app/json/Makefile | 46 + app/json/jsonparse.c | 281 ++ app/json/jsontree.c | 296 +++ app/libc/Makefile | 44 + app/libc/c_ctype.c | 16 + app/libc/c_ctype.h | 42 + app/libc/c_errno.h | 19 + app/libc/c_fcntl.h | 9 + app/libc/c_limits.h | 58 + app/libc/c_locale.h | 62 + app/libc/c_math.c | 80 + app/libc/c_math.h | 58 + app/libc/c_signal.h | 6 + app/libc/c_stdarg.h | 14 + app/libc/c_stddef.h | 23 + app/libc/c_stdint.h | 273 ++ app/libc/c_stdio.c | 56 + app/libc/c_stdio.h | 92 + app/libc/c_stdlib.c | 60 + app/libc/c_stdlib.h | 63 + app/libc/c_string.c | 16 + app/libc/c_string.h | 43 + app/lua/Makefile | 48 + app/lua/compiler.h | 47 + app/lua/lapi.c | 1161 ++++++++ app/lua/lapi.h | 16 + app/lua/lauxlib.c | 819 ++++++ app/lua/lauxlib.h | 188 ++ app/lua/lbaselib.c | 722 +++++ app/lua/lcode.c | 831 ++++++ app/lua/lcode.h | 76 + app/lua/ldebug.c | 649 +++++ app/lua/ldebug.h | 33 + app/lua/ldo.c | 537 ++++ app/lua/ldo.h | 57 + app/lua/ldump.c | 320 +++ app/lua/legc.c | 13 + app/lua/legc.h | 17 + app/lua/lfunc.c | 176 ++ app/lua/lfunc.h | 37 + app/lua/lgc.c | 743 ++++++ app/lua/lgc.h | 136 + app/lua/linit.c | 82 + app/lua/liolib.c | 659 +++++ app/lua/llex.c | 458 ++++ app/lua/llex.h | 81 + app/lua/llimits.h | 128 + app/lua/lmathlib.c | 394 +++ app/lua/lmem.c | 86 + app/lua/lmem.h | 49 + app/lua/loadlib.c | 695 +++++ app/lua/lobject.c | 216 ++ app/lua/lobject.h | 551 ++++ app/lua/lopcodes.c | 102 + app/lua/lopcodes.h | 268 ++ app/lua/lparser.c | 1345 ++++++++++ app/lua/lparser.h | 82 + app/lua/lrodefs.h | 41 + app/lua/lrotable.c | 135 + app/lua/lrotable.h | 77 + app/lua/lstate.c | 246 ++ app/lua/lstate.h | 171 ++ app/lua/lstring.c | 147 ++ app/lua/lstring.h | 34 + app/lua/lstrlib.c | 893 +++++++ app/lua/ltable.c | 759 ++++++ app/lua/ltable.h | 44 + app/lua/ltablib.c | 287 ++ app/lua/ltm.c | 84 + app/lua/ltm.h | 54 + app/lua/lua.c | 699 +++++ app/lua/lua.h | 409 +++ app/lua/luaconf.h | 888 +++++++ app/lua/lualib.h | 53 + app/lua/lundump.c | 338 +++ app/lua/lundump.h | 60 + app/lua/lvm.c | 825 ++++++ app/lua/lvm.h | 36 + app/lua/lzio.c | 85 + app/lua/lzio.h | 72 + app/lua/not_ported/loslib.c | 247 ++ app/lua/not_ported/luac.c | 242 ++ app/lua/not_ported/print.c | 227 ++ app/lua/not_used/ldblib.c | 406 +++ app/lwip/Makefile | 50 + app/lwip/api/Makefile | 46 + app/lwip/api/api_lib.c | 740 ++++++ app/lwip/api/api_msg.c | 1540 +++++++++++ app/lwip/api/err.c | 75 + app/lwip/api/netbuf.c | 245 ++ app/lwip/api/netdb.c | 352 +++ app/lwip/api/netifapi.c | 160 ++ app/lwip/api/sockets.c | 2343 +++++++++++++++++ app/lwip/api/tcpip.c | 460 ++++ app/lwip/app/Makefile | 46 + app/lwip/app/dhcpserver.c | 858 ++++++ app/lwip/app/espconn.c | 719 +++++ app/lwip/app/espconn_tcp.c | 954 +++++++ app/lwip/app/espconn_udp.c | 298 +++ app/lwip/app/netio.c | 365 +++ app/lwip/app/ping.c | 322 +++ app/lwip/core/Makefile | 46 + app/lwip/core/def.c | 108 + app/lwip/core/dhcp.c | 1766 +++++++++++++ app/lwip/core/dns.c | 970 +++++++ app/lwip/core/init.c | 321 +++ app/lwip/core/ipv4/Makefile | 46 + app/lwip/core/ipv4/autoip.c | 536 ++++ app/lwip/core/ipv4/icmp.c | 338 +++ app/lwip/core/ipv4/igmp.c | 817 ++++++ app/lwip/core/ipv4/inet.c | 42 + app/lwip/core/ipv4/inet_chksum.c | 450 ++++ app/lwip/core/ipv4/ip.c | 904 +++++++ app/lwip/core/ipv4/ip_addr.c | 312 +++ app/lwip/core/ipv4/ip_frag.c | 863 ++++++ app/lwip/core/mem.c | 644 +++++ app/lwip/core/memp.c | 490 ++++ app/lwip/core/netif.c | 757 ++++++ app/lwip/core/pbuf.c | 1212 +++++++++ app/lwip/core/raw.c | 354 +++ app/lwip/core/stats.c | 176 ++ app/lwip/core/sys.c | 66 + app/lwip/core/sys_arch.c | 13 + app/lwip/core/tcp.c | 1651 ++++++++++++ app/lwip/core/tcp_in.c | 1618 ++++++++++++ app/lwip/core/tcp_out.c | 1472 +++++++++++ app/lwip/core/timers.c | 509 ++++ app/lwip/core/udp.c | 964 +++++++ app/lwip/netif/Makefile | 46 + app/lwip/netif/etharp.c | 1357 ++++++++++ app/modules/Makefile | 49 + app/modules/adc.c | 44 + app/modules/auxmods.h | 93 + app/modules/bit.c | 145 + app/modules/file.c | 262 ++ app/modules/gpio.c | 200 ++ app/modules/i2c.c | 181 ++ app/modules/modules.h | 119 + app/modules/net.c | 1359 ++++++++++ app/modules/node.c | 262 ++ app/modules/ow.c | 327 +++ app/modules/pwm.c | 154 ++ app/modules/tmr.c | 189 ++ app/modules/uart.c | 148 ++ app/modules/wifi.c | 431 +++ app/platform/Makefile | 47 + app/platform/common.c | 260 ++ app/platform/common.h | 18 + app/platform/cpu_esp8266.h | 54 + app/platform/flash_fs.c | 32 + app/platform/flash_fs.h | 78 + app/platform/pin_map.c | 45 + app/platform/pin_map.h | 17 + app/platform/platform.c | 483 ++++ app/platform/platform.h | 232 ++ app/smart/Makefile | 44 + app/smart/smart.c | 713 +++++ app/smart/smart.h | 87 + app/spiffs/Makefile | 44 + app/spiffs/docs/IMPLEMENTING | 0 app/spiffs/docs/INTEGRATION | 283 ++ app/spiffs/docs/TECH_SPEC | 239 ++ app/spiffs/docs/TODO | 1 + app/spiffs/params_test.h | 36 + app/spiffs/spiffs.c | 159 ++ app/spiffs/spiffs.h | 442 ++++ app/spiffs/spiffs_cache.c | 301 +++ app/spiffs/spiffs_check.c | 967 +++++++ app/spiffs/spiffs_config.h | 210 ++ app/spiffs/spiffs_gc.c | 550 ++++ app/spiffs/spiffs_hydrogen.c | 790 ++++++ app/spiffs/spiffs_nucleus.c | 1760 +++++++++++++ app/spiffs/spiffs_nucleus.h | 684 +++++ app/ssl/Makefile | 48 + app/ssl/app/Makefile | 46 + app/ssl/app/espconn_secure.c | 97 + app/ssl/app/espconn_ssl.c | 1100 ++++++++ app/ssl/crypto/Makefile | 46 + app/ssl/crypto/ssl_aes.c | 460 ++++ app/ssl/crypto/ssl_bigint.c | 1514 +++++++++++ app/ssl/crypto/ssl_crypto_misc.c | 372 +++ app/ssl/crypto/ssl_hmac.c | 105 + app/ssl/crypto/ssl_md2.c | 163 ++ app/ssl/crypto/ssl_md5.c | 295 +++ app/ssl/crypto/ssl_rc4.c | 92 + app/ssl/crypto/ssl_rsa.c | 271 ++ app/ssl/crypto/ssl_sha1.c | 250 ++ app/ssl/ssl/Makefile | 46 + app/ssl/ssl/ssl_asn1.c | 591 +++++ app/ssl/ssl/ssl_gen_cert.c | 364 +++ app/ssl/ssl/ssl_loader.c | 487 ++++ app/ssl/ssl/ssl_openssl.c | 322 +++ app/ssl/ssl/ssl_os_port.c | 176 ++ app/ssl/ssl/ssl_p12.c | 483 ++++ app/ssl/ssl/ssl_tls1.c | 2250 ++++++++++++++++ app/ssl/ssl/ssl_tls1_clnt.c | 421 +++ app/ssl/ssl/ssl_tls1_svr.c | 499 ++++ app/ssl/ssl/ssl_x509.c | 565 ++++ app/upgrade/Makefile | 47 + app/upgrade/upgrade.c | 317 +++ app/upgrade/upgrade_lib.c | 145 + app/user/Makefile | 49 + app/user/user_main.c | 110 + app/wofs/Makefile | 44 + app/wofs/romfiles.h | 12 + app/wofs/romfs.c | 565 ++++ app/wofs/romfs.h | 101 + bin/.gitignore | 7 + examples/fragment.lua | 303 +++ examples/{telnet2.lua => tel.lua} | 0 examples/telnet.lua | 42 +- include/c_types.h | 96 + include/eagle_soc.h | 256 ++ include/espconn.h | 375 +++ include/ets_sys.h | 90 + include/gpio.h | 100 + include/ip_addr.h | 64 + include/json/json.h | 70 + include/json/jsonparse.h | 94 + include/json/jsontree.h | 145 + include/mem.h | 12 + include/os_type.h | 19 + include/osapi.h | 48 + include/queue.h | 204 ++ include/spi_flash.h | 31 + include/upgrade.h | 51 + include/user_interface.h | 252 ++ ld/eagle.app.v6.app1.ld | 181 ++ ld/eagle.app.v6.app2.ld | 181 ++ ld/eagle.app.v6.ld | 185 ++ ld/eagle.rom.addr.v6.ld | 344 +++ lib/libjson.a | Bin 0 -> 12714 bytes lib/liblwip.a | Bin 0 -> 248074 bytes lib/libmain.a | Bin 0 -> 85278 bytes lib/libnet80211.a | Bin 0 -> 182112 bytes lib/libphy.a | Bin 0 -> 136808 bytes lib/libpp.a | Bin 0 -> 165592 bytes lib/libssl.a | Bin 0 -> 159426 bytes lib/libupgrade.a | Bin 0 -> 14868 bytes lib/libwpa.a | Bin 0 -> 124372 bytes {examples => lua examples}/myfile.lua | 0 .../onewire-ds18b20.lua | 0 lua examples/telnet.lua | 39 + lua examples/telnet2.lua | 17 + {modules => lua modules}/bmp085.lua | 314 +-- .../ds18b20/ds18b20.CN.md | 0 .../ds18b20/ds18b20.EN.md | 0 {modules => lua modules}/ds18b20/ds18b20.lua | 0 {0.9.2 => prebuild0.9.2}/1M-flash/README.md | 0 .../1M-flash/nodemcu_1M_20141219.bin | Bin {0.9.2 => prebuild0.9.2}/2M-flash/README.md | 0 {0.9.2 => prebuild0.9.2}/2M-flash/blank.bin | 0 .../2M-flash/eagle.app.v6.flash.bin | Bin .../2M-flash/eagle.app.v6.irom0text.bin | Bin .../2M-flash/esp_init_data_default.bin | Bin .../2M-flash/nodemcu_2M_20141219.bin | Bin {0.9.2 => prebuild0.9.2}/4M-flash/README.md | 0 {0.9.2 => prebuild0.9.2}/4M-flash/blank.bin | 0 .../4M-flash/eagle.app.v6.flash.bin | Bin .../4M-flash/eagle.app.v6.irom0text.bin | Bin .../4M-flash/esp_init_data_default.bin | Bin {0.9.2 => prebuild0.9.2}/512k-flash/README.md | 0 .../512k-flash/blank512k.bin | 0 .../512k-flash/nodemcu_512k_20141212.bin | Bin .../512k-flash/nodemcu_512k_20141219.bin | Bin tools/gen_appbin.py | 71 + tools/gen_flashbin.py | 33 + tools/genflashbinv6.exe | Bin 0 -> 13824 bytes tools/makefile.sh | 11 + tools/xxd.exe | Bin 0 -> 70144 bytes 369 files changed, 96570 insertions(+), 186 deletions(-) create mode 100644 Makefile create mode 100644 app/.gitignore create mode 100644 app/Makefile create mode 100644 app/driver/Makefile create mode 100644 app/driver/gpio16.c create mode 100644 app/driver/i2c_master.c create mode 100644 app/driver/key.c create mode 100644 app/driver/onewire.c create mode 100644 app/driver/pwm.c create mode 100644 app/driver/readline.c create mode 100644 app/driver/spi.c create mode 100644 app/driver/uart.c create mode 100644 app/gen_misc.bat create mode 100644 app/gen_misc.sh create mode 100644 app/gen_misc_plus.bat create mode 100644 app/gen_misc_plus.sh create mode 100644 app/include/arch/cc.h create mode 100644 app/include/arch/perf.h create mode 100644 app/include/arch/sys_arch.h create mode 100644 app/include/driver/gpio16.h create mode 100644 app/include/driver/i2c_master.h create mode 100644 app/include/driver/key.h create mode 100644 app/include/driver/onewire.h create mode 100644 app/include/driver/pwm.h create mode 100644 app/include/driver/spi.h create mode 100644 app/include/driver/spi_master.h create mode 100644 app/include/driver/spi_register.h create mode 100644 app/include/driver/uart.h create mode 100644 app/include/driver/uart_register.h create mode 100644 app/include/lwip/api.h create mode 100644 app/include/lwip/api_msg.h create mode 100644 app/include/lwip/app/dhcpserver.h create mode 100644 app/include/lwip/app/espconn.h create mode 100644 app/include/lwip/app/espconn_tcp.h create mode 100644 app/include/lwip/app/espconn_udp.h create mode 100644 app/include/lwip/app/ping.h create mode 100644 app/include/lwip/arch.h create mode 100644 app/include/lwip/autoip.h create mode 100644 app/include/lwip/debug.h create mode 100644 app/include/lwip/def.h create mode 100644 app/include/lwip/dhcp.h create mode 100644 app/include/lwip/dns.h create mode 100644 app/include/lwip/err.h create mode 100644 app/include/lwip/icmp.h create mode 100644 app/include/lwip/igmp.h create mode 100644 app/include/lwip/inet.h create mode 100644 app/include/lwip/inet_chksum.h create mode 100644 app/include/lwip/init.h create mode 100644 app/include/lwip/ip.h create mode 100644 app/include/lwip/ip_addr.h create mode 100644 app/include/lwip/ip_frag.h create mode 100644 app/include/lwip/mem.h create mode 100644 app/include/lwip/memp.h create mode 100644 app/include/lwip/memp_std.h create mode 100644 app/include/lwip/netbuf.h create mode 100644 app/include/lwip/netdb.h create mode 100644 app/include/lwip/netif.h create mode 100644 app/include/lwip/netifapi.h create mode 100644 app/include/lwip/opt.h create mode 100644 app/include/lwip/pbuf.h create mode 100644 app/include/lwip/raw.h create mode 100644 app/include/lwip/sio.h create mode 100644 app/include/lwip/snmp.h create mode 100644 app/include/lwip/snmp_asn1.h create mode 100644 app/include/lwip/snmp_msg.h create mode 100644 app/include/lwip/snmp_structs.h create mode 100644 app/include/lwip/sockets.h create mode 100644 app/include/lwip/stats.h create mode 100644 app/include/lwip/sys.h create mode 100644 app/include/lwip/tcp.h create mode 100644 app/include/lwip/tcp_impl.h create mode 100644 app/include/lwip/tcpip.h create mode 100644 app/include/lwip/timers.h create mode 100644 app/include/lwip/udp.h create mode 100644 app/include/lwipopts.h create mode 100644 app/include/mem_manager.h create mode 100644 app/include/netif/etharp.h create mode 100644 app/include/netif/if_llc.h create mode 100644 app/include/netif/ppp_oe.h create mode 100644 app/include/netif/wlan_lwip_if.h create mode 100644 app/include/pp/esf_buf.h create mode 100644 app/include/ssl/app/espconn_secure.h create mode 100644 app/include/ssl/app/espconn_ssl.h create mode 100644 app/include/ssl/cert.h create mode 100644 app/include/ssl/private_key.h create mode 100644 app/include/ssl/ssl_bigint.h create mode 100644 app/include/ssl/ssl_bigint_impl.h create mode 100644 app/include/ssl/ssl_cert.h create mode 100644 app/include/ssl/ssl_config.h create mode 100644 app/include/ssl/ssl_crypto.h create mode 100644 app/include/ssl/ssl_crypto_misc.h create mode 100644 app/include/ssl/ssl_os_int.h create mode 100644 app/include/ssl/ssl_os_port.h create mode 100644 app/include/ssl/ssl_private_key.h create mode 100644 app/include/ssl/ssl_ssl.h create mode 100644 app/include/ssl/ssl_tls1.h create mode 100644 app/include/ssl/ssl_version.h create mode 100644 app/include/user_config.h create mode 100644 app/json/Makefile create mode 100644 app/json/jsonparse.c create mode 100644 app/json/jsontree.c create mode 100644 app/libc/Makefile create mode 100644 app/libc/c_ctype.c create mode 100644 app/libc/c_ctype.h create mode 100644 app/libc/c_errno.h create mode 100644 app/libc/c_fcntl.h create mode 100644 app/libc/c_limits.h create mode 100644 app/libc/c_locale.h create mode 100644 app/libc/c_math.c create mode 100644 app/libc/c_math.h create mode 100644 app/libc/c_signal.h create mode 100644 app/libc/c_stdarg.h create mode 100644 app/libc/c_stddef.h create mode 100644 app/libc/c_stdint.h create mode 100644 app/libc/c_stdio.c create mode 100644 app/libc/c_stdio.h create mode 100644 app/libc/c_stdlib.c create mode 100644 app/libc/c_stdlib.h create mode 100644 app/libc/c_string.c create mode 100644 app/libc/c_string.h create mode 100644 app/lua/Makefile create mode 100644 app/lua/compiler.h create mode 100644 app/lua/lapi.c create mode 100644 app/lua/lapi.h create mode 100644 app/lua/lauxlib.c create mode 100644 app/lua/lauxlib.h create mode 100644 app/lua/lbaselib.c create mode 100644 app/lua/lcode.c create mode 100644 app/lua/lcode.h create mode 100644 app/lua/ldebug.c create mode 100644 app/lua/ldebug.h create mode 100644 app/lua/ldo.c create mode 100644 app/lua/ldo.h create mode 100644 app/lua/ldump.c create mode 100644 app/lua/legc.c create mode 100644 app/lua/legc.h create mode 100644 app/lua/lfunc.c create mode 100644 app/lua/lfunc.h create mode 100644 app/lua/lgc.c create mode 100644 app/lua/lgc.h create mode 100644 app/lua/linit.c create mode 100644 app/lua/liolib.c create mode 100644 app/lua/llex.c create mode 100644 app/lua/llex.h create mode 100644 app/lua/llimits.h create mode 100644 app/lua/lmathlib.c create mode 100644 app/lua/lmem.c create mode 100644 app/lua/lmem.h create mode 100644 app/lua/loadlib.c create mode 100644 app/lua/lobject.c create mode 100644 app/lua/lobject.h create mode 100644 app/lua/lopcodes.c create mode 100644 app/lua/lopcodes.h create mode 100644 app/lua/lparser.c create mode 100644 app/lua/lparser.h create mode 100644 app/lua/lrodefs.h create mode 100644 app/lua/lrotable.c create mode 100644 app/lua/lrotable.h create mode 100644 app/lua/lstate.c create mode 100644 app/lua/lstate.h create mode 100644 app/lua/lstring.c create mode 100644 app/lua/lstring.h create mode 100644 app/lua/lstrlib.c create mode 100644 app/lua/ltable.c create mode 100644 app/lua/ltable.h create mode 100644 app/lua/ltablib.c create mode 100644 app/lua/ltm.c create mode 100644 app/lua/ltm.h create mode 100644 app/lua/lua.c create mode 100644 app/lua/lua.h create mode 100644 app/lua/luaconf.h create mode 100644 app/lua/lualib.h create mode 100644 app/lua/lundump.c create mode 100644 app/lua/lundump.h create mode 100644 app/lua/lvm.c create mode 100644 app/lua/lvm.h create mode 100644 app/lua/lzio.c create mode 100644 app/lua/lzio.h create mode 100644 app/lua/not_ported/loslib.c create mode 100644 app/lua/not_ported/luac.c create mode 100644 app/lua/not_ported/print.c create mode 100644 app/lua/not_used/ldblib.c create mode 100644 app/lwip/Makefile create mode 100644 app/lwip/api/Makefile create mode 100644 app/lwip/api/api_lib.c create mode 100644 app/lwip/api/api_msg.c create mode 100644 app/lwip/api/err.c create mode 100644 app/lwip/api/netbuf.c create mode 100644 app/lwip/api/netdb.c create mode 100644 app/lwip/api/netifapi.c create mode 100644 app/lwip/api/sockets.c create mode 100644 app/lwip/api/tcpip.c create mode 100644 app/lwip/app/Makefile create mode 100644 app/lwip/app/dhcpserver.c create mode 100644 app/lwip/app/espconn.c create mode 100644 app/lwip/app/espconn_tcp.c create mode 100644 app/lwip/app/espconn_udp.c create mode 100644 app/lwip/app/netio.c create mode 100644 app/lwip/app/ping.c create mode 100644 app/lwip/core/Makefile create mode 100644 app/lwip/core/def.c create mode 100644 app/lwip/core/dhcp.c create mode 100644 app/lwip/core/dns.c create mode 100644 app/lwip/core/init.c create mode 100644 app/lwip/core/ipv4/Makefile create mode 100644 app/lwip/core/ipv4/autoip.c create mode 100644 app/lwip/core/ipv4/icmp.c create mode 100644 app/lwip/core/ipv4/igmp.c create mode 100644 app/lwip/core/ipv4/inet.c create mode 100644 app/lwip/core/ipv4/inet_chksum.c create mode 100644 app/lwip/core/ipv4/ip.c create mode 100644 app/lwip/core/ipv4/ip_addr.c create mode 100644 app/lwip/core/ipv4/ip_frag.c create mode 100644 app/lwip/core/mem.c create mode 100644 app/lwip/core/memp.c create mode 100644 app/lwip/core/netif.c create mode 100644 app/lwip/core/pbuf.c create mode 100644 app/lwip/core/raw.c create mode 100644 app/lwip/core/stats.c create mode 100644 app/lwip/core/sys.c create mode 100644 app/lwip/core/sys_arch.c create mode 100644 app/lwip/core/tcp.c create mode 100644 app/lwip/core/tcp_in.c create mode 100644 app/lwip/core/tcp_out.c create mode 100644 app/lwip/core/timers.c create mode 100644 app/lwip/core/udp.c create mode 100644 app/lwip/netif/Makefile create mode 100644 app/lwip/netif/etharp.c create mode 100644 app/modules/Makefile create mode 100644 app/modules/adc.c create mode 100644 app/modules/auxmods.h create mode 100644 app/modules/bit.c create mode 100644 app/modules/file.c create mode 100644 app/modules/gpio.c create mode 100644 app/modules/i2c.c create mode 100644 app/modules/modules.h create mode 100644 app/modules/net.c create mode 100644 app/modules/node.c create mode 100644 app/modules/ow.c create mode 100644 app/modules/pwm.c create mode 100644 app/modules/tmr.c create mode 100644 app/modules/uart.c create mode 100644 app/modules/wifi.c create mode 100644 app/platform/Makefile create mode 100644 app/platform/common.c create mode 100644 app/platform/common.h create mode 100644 app/platform/cpu_esp8266.h create mode 100644 app/platform/flash_fs.c create mode 100644 app/platform/flash_fs.h create mode 100644 app/platform/pin_map.c create mode 100644 app/platform/pin_map.h create mode 100644 app/platform/platform.c create mode 100644 app/platform/platform.h create mode 100644 app/smart/Makefile create mode 100644 app/smart/smart.c create mode 100644 app/smart/smart.h create mode 100644 app/spiffs/Makefile create mode 100644 app/spiffs/docs/IMPLEMENTING create mode 100644 app/spiffs/docs/INTEGRATION create mode 100644 app/spiffs/docs/TECH_SPEC create mode 100644 app/spiffs/docs/TODO create mode 100644 app/spiffs/params_test.h create mode 100644 app/spiffs/spiffs.c create mode 100644 app/spiffs/spiffs.h create mode 100644 app/spiffs/spiffs_cache.c create mode 100644 app/spiffs/spiffs_check.c create mode 100644 app/spiffs/spiffs_config.h create mode 100644 app/spiffs/spiffs_gc.c create mode 100644 app/spiffs/spiffs_hydrogen.c create mode 100644 app/spiffs/spiffs_nucleus.c create mode 100644 app/spiffs/spiffs_nucleus.h create mode 100644 app/ssl/Makefile create mode 100644 app/ssl/app/Makefile create mode 100644 app/ssl/app/espconn_secure.c create mode 100644 app/ssl/app/espconn_ssl.c create mode 100644 app/ssl/crypto/Makefile create mode 100644 app/ssl/crypto/ssl_aes.c create mode 100644 app/ssl/crypto/ssl_bigint.c create mode 100644 app/ssl/crypto/ssl_crypto_misc.c create mode 100644 app/ssl/crypto/ssl_hmac.c create mode 100644 app/ssl/crypto/ssl_md2.c create mode 100644 app/ssl/crypto/ssl_md5.c create mode 100644 app/ssl/crypto/ssl_rc4.c create mode 100644 app/ssl/crypto/ssl_rsa.c create mode 100644 app/ssl/crypto/ssl_sha1.c create mode 100644 app/ssl/ssl/Makefile create mode 100644 app/ssl/ssl/ssl_asn1.c create mode 100644 app/ssl/ssl/ssl_gen_cert.c create mode 100644 app/ssl/ssl/ssl_loader.c create mode 100644 app/ssl/ssl/ssl_openssl.c create mode 100644 app/ssl/ssl/ssl_os_port.c create mode 100644 app/ssl/ssl/ssl_p12.c create mode 100644 app/ssl/ssl/ssl_tls1.c create mode 100644 app/ssl/ssl/ssl_tls1_clnt.c create mode 100644 app/ssl/ssl/ssl_tls1_svr.c create mode 100644 app/ssl/ssl/ssl_x509.c create mode 100644 app/upgrade/Makefile create mode 100644 app/upgrade/upgrade.c create mode 100644 app/upgrade/upgrade_lib.c create mode 100644 app/user/Makefile create mode 100644 app/user/user_main.c create mode 100644 app/wofs/Makefile create mode 100644 app/wofs/romfiles.h create mode 100644 app/wofs/romfs.c create mode 100644 app/wofs/romfs.h create mode 100644 bin/.gitignore create mode 100644 examples/fragment.lua rename examples/{telnet2.lua => tel.lua} (100%) create mode 100644 include/c_types.h create mode 100644 include/eagle_soc.h create mode 100644 include/espconn.h create mode 100644 include/ets_sys.h create mode 100644 include/gpio.h create mode 100644 include/ip_addr.h create mode 100644 include/json/json.h create mode 100644 include/json/jsonparse.h create mode 100644 include/json/jsontree.h create mode 100644 include/mem.h create mode 100644 include/os_type.h create mode 100644 include/osapi.h create mode 100644 include/queue.h create mode 100644 include/spi_flash.h create mode 100644 include/upgrade.h create mode 100644 include/user_interface.h create mode 100644 ld/eagle.app.v6.app1.ld create mode 100644 ld/eagle.app.v6.app2.ld create mode 100644 ld/eagle.app.v6.ld create mode 100644 ld/eagle.rom.addr.v6.ld create mode 100644 lib/libjson.a create mode 100644 lib/liblwip.a create mode 100644 lib/libmain.a create mode 100644 lib/libnet80211.a create mode 100644 lib/libphy.a create mode 100644 lib/libpp.a create mode 100644 lib/libssl.a create mode 100644 lib/libupgrade.a create mode 100644 lib/libwpa.a rename {examples => lua examples}/myfile.lua (100%) rename {examples => lua examples}/onewire-ds18b20.lua (100%) create mode 100644 lua examples/telnet.lua create mode 100644 lua examples/telnet2.lua rename {modules => lua modules}/bmp085.lua (96%) rename {modules => lua modules}/ds18b20/ds18b20.CN.md (100%) rename {modules => lua modules}/ds18b20/ds18b20.EN.md (100%) rename {modules => lua modules}/ds18b20/ds18b20.lua (100%) rename {0.9.2 => prebuild0.9.2}/1M-flash/README.md (100%) rename {0.9.2 => prebuild0.9.2}/1M-flash/nodemcu_1M_20141219.bin (100%) rename {0.9.2 => prebuild0.9.2}/2M-flash/README.md (100%) rename {0.9.2 => prebuild0.9.2}/2M-flash/blank.bin (100%) rename {0.9.2 => prebuild0.9.2}/2M-flash/eagle.app.v6.flash.bin (100%) rename {0.9.2 => prebuild0.9.2}/2M-flash/eagle.app.v6.irom0text.bin (100%) rename {0.9.2 => prebuild0.9.2}/2M-flash/esp_init_data_default.bin (100%) rename {0.9.2 => prebuild0.9.2}/2M-flash/nodemcu_2M_20141219.bin (100%) rename {0.9.2 => prebuild0.9.2}/4M-flash/README.md (100%) rename {0.9.2 => prebuild0.9.2}/4M-flash/blank.bin (100%) rename {0.9.2 => prebuild0.9.2}/4M-flash/eagle.app.v6.flash.bin (100%) rename {0.9.2 => prebuild0.9.2}/4M-flash/eagle.app.v6.irom0text.bin (100%) rename {0.9.2 => prebuild0.9.2}/4M-flash/esp_init_data_default.bin (100%) rename {0.9.2 => prebuild0.9.2}/512k-flash/README.md (100%) rename blank512k.bin => prebuild0.9.2/512k-flash/blank512k.bin (100%) rename {0.9.2 => prebuild0.9.2}/512k-flash/nodemcu_512k_20141212.bin (100%) rename {0.9.2 => prebuild0.9.2}/512k-flash/nodemcu_512k_20141219.bin (100%) create mode 100644 tools/gen_appbin.py create mode 100644 tools/gen_flashbin.py create mode 100644 tools/genflashbinv6.exe create mode 100644 tools/makefile.sh create mode 100644 tools/xxd.exe diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..0c94576c --- /dev/null +++ b/Makefile @@ -0,0 +1,174 @@ +# copyright (c) 2010 Espressif System +# +ifndef PDIR + +endif + +AR = xt-ar +CC = xt-xcc +NM = xt-nm +CPP = xt-cpp +OBJCOPY = xt-objcopy +#MAKE = xt-make + +CSRCS ?= $(wildcard *.c) +ASRCs ?= $(wildcard *.s) +ASRCS ?= $(wildcard *.S) +SUBDIRS ?= $(patsubst %/,%,$(dir $(wildcard */Makefile))) + +ODIR := .output +OBJODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/obj + +OBJS := $(CSRCS:%.c=$(OBJODIR)/%.o) \ + $(ASRCs:%.s=$(OBJODIR)/%.o) \ + $(ASRCS:%.S=$(OBJODIR)/%.o) + +DEPS := $(CSRCS:%.c=$(OBJODIR)/%.d) \ + $(ASRCs:%.s=$(OBJODIR)/%.d) \ + $(ASRCS:%.S=$(OBJODIR)/%.d) + +LIBODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/lib +OLIBS := $(GEN_LIBS:%=$(LIBODIR)/%) + +IMAGEODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/image +OIMAGES := $(GEN_IMAGES:%=$(IMAGEODIR)/%) + +BINODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/bin +OBINS := $(GEN_BINS:%=$(BINODIR)/%) + +CCFLAGS += \ + -g \ + -O2 \ + -Wpointer-arith \ + -Wundef \ + -Werror \ + -Wl,-EL \ + -fno-inline-functions \ + -nostdlib \ + -mlongcalls \ + -mtext-section-literals +# -Wall + +CFLAGS = $(CCFLAGS) $(DEFINES) $(EXTRA_CCFLAGS) $(INCLUDES) +DFLAGS = $(CCFLAGS) $(DDEFINES) $(EXTRA_CCFLAGS) $(INCLUDES) + + +############################################################# +# Functions +# + +define ShortcutRule +$(1): .subdirs $(2)/$(1) +endef + +define MakeLibrary +DEP_LIBS_$(1) = $$(foreach lib,$$(filter %.a,$$(COMPONENTS_$(1))),$$(dir $$(lib))$$(LIBODIR)/$$(notdir $$(lib))) +DEP_OBJS_$(1) = $$(foreach obj,$$(filter %.o,$$(COMPONENTS_$(1))),$$(dir $$(obj))$$(OBJODIR)/$$(notdir $$(obj))) +$$(LIBODIR)/$(1).a: $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1)) $$(DEPENDS_$(1)) + @mkdir -p $$(LIBODIR) + $$(if $$(filter %.a,$$?),mkdir -p $$(EXTRACT_DIR)_$(1)) + $$(if $$(filter %.a,$$?),cd $$(EXTRACT_DIR)_$(1); $$(foreach lib,$$(filter %.a,$$?),$$(AR) xo $$(UP_EXTRACT_DIR)/$$(lib);)) + $$(AR) ru $$@ $$(filter %.o,$$?) $$(if $$(filter %.a,$$?),$$(EXTRACT_DIR)_$(1)/*.o) + $$(if $$(filter %.a,$$?),$$(RM) -r $$(EXTRACT_DIR)_$(1)) +endef + +define MakeImage +DEP_LIBS_$(1) = $$(foreach lib,$$(filter %.a,$$(COMPONENTS_$(1))),$$(dir $$(lib))$$(LIBODIR)/$$(notdir $$(lib))) +DEP_OBJS_$(1) = $$(foreach obj,$$(filter %.o,$$(COMPONENTS_$(1))),$$(dir $$(obj))$$(OBJODIR)/$$(notdir $$(obj))) +$$(IMAGEODIR)/$(1).out: $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1)) $$(DEPENDS_$(1)) + @mkdir -p $$(IMAGEODIR) + $$(CC) $$(LDFLAGS) $$(if $$(LINKFLAGS_$(1)),$$(LINKFLAGS_$(1)),$$(LINKFLAGS_DEFAULT) $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1))) -o $$@ +endef + +$(BINODIR)/%.bin: $(IMAGEODIR)/%.out + @mkdir -p $(BINODIR) + $(OBJCOPY) -O binary $< $@ + +############################################################# +# Rules base +# Should be done in top-level makefile only +# + +all: .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS) + +clean: + $(foreach d, $(SUBDIRS), $(MAKE) -C $(d) clean;) + $(RM) -r $(ODIR)/$(TARGET)/$(FLAVOR) + +clobber: $(SPECIAL_CLOBBER) + $(foreach d, $(SUBDIRS), $(MAKE) -C $(d) clobber;) + $(RM) -r $(ODIR) + +.subdirs: + @set -e; $(foreach d, $(SUBDIRS), $(MAKE) -C $(d);) + +#.subdirs: +# $(foreach d, $(SUBDIRS), $(MAKE) -C $(d)) + +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(MAKECMDGOALS),clobber) +ifdef DEPS +sinclude $(DEPS) +endif +endif +endif + +$(OBJODIR)/%.o: %.c + @mkdir -p $(OBJODIR); + $(CC) $(if $(findstring $<,$(DSRCS)),$(DFLAGS),$(CFLAGS)) $(COPTS_$(*F)) -o $@ -c $< + +$(OBJODIR)/%.d: %.c + @mkdir -p $(OBJODIR); + @echo DEPEND: $(CC) -M $(CFLAGS) $< + @set -e; rm -f $@; \ + $(CC) -M $(CFLAGS) $< > $@.$$$$; \ + sed 's,\($*\.o\)[ :]*,$(OBJODIR)/\1 $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +$(OBJODIR)/%.o: %.s + @mkdir -p $(OBJODIR); + $(CC) $(CFLAGS) -o $@ -c $< + +$(OBJODIR)/%.d: %.s + @mkdir -p $(OBJODIR); \ + set -e; rm -f $@; \ + $(CC) -M $(CFLAGS) $< > $@.$$$$; \ + sed 's,\($*\.o\)[ :]*,$(OBJODIR)/\1 $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +$(OBJODIR)/%.o: %.S + @mkdir -p $(OBJODIR); + $(CC) $(CFLAGS) -D__ASSEMBLER__ -o $@ -c $< + +$(OBJODIR)/%.d: %.S + @mkdir -p $(OBJODIR); \ + set -e; rm -f $@; \ + $(CC) -M $(CFLAGS) $< > $@.$$$$; \ + sed 's,\($*\.o\)[ :]*,$(OBJODIR)/\1 $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +$(foreach lib,$(GEN_LIBS),$(eval $(call ShortcutRule,$(lib),$(LIBODIR)))) + +$(foreach image,$(GEN_IMAGES),$(eval $(call ShortcutRule,$(image),$(IMAGEODIR)))) + +$(foreach bin,$(GEN_BINS),$(eval $(call ShortcutRule,$(bin),$(BINODIR)))) + +$(foreach lib,$(GEN_LIBS),$(eval $(call MakeLibrary,$(basename $(lib))))) + +$(foreach image,$(GEN_IMAGES),$(eval $(call MakeImage,$(basename $(image))))) + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include -I $(PDIR)include/$(TARGET) +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile diff --git a/README.md b/README.md index 9e650c42..0e7a99bc 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,26 @@ # **NodeMcu** # +version 0.9.4 ###A lua based firmware for wifi-soc esp8266 -version 0.9.2 build 2014-12-19 +Build on [ESP8266 sdk 0.9.4](http://bbs.espressif.com/viewtopic.php?f=5&t=90) +Lua core based on [eLua project](http://www.eluaproject.net/) +File system based on [spiffs](https://github.com/pellepl/spiffs) +Open source development kit for NodeMCU [nodemcu-devkit](https://github.com/nodemcu/nodemcu-devkit) +Flash tool for NodeMCU [nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher) + +wiki: [nodemcu wiki](https://github.com/nodemcu/nodemcu-firmware/wiki) +home: [nodemcu.com](http://www.nodemcu.com) +bbs: [中文论坛Chinese bbs](http://bbs.nodemcu.com) +Tencent QQ group QQ群: 309957875 + # Change log +2014-12-22
+update to sdk 0.9.4
+opensource + 2014-12-19
**Important** Re-arrange GPIO MAP due to development kit.[New Gpio Map](#new_gpio_map)
Add bitwise operation module.
-Modify net.socket:connect() api to accept domain name, auto DNS.
-Add firmware for flash size 1Mbytes, 2Mbytes, 4Mbytes. +Modify net.socket:connect() api to accept domain name, auto DNS. [more change log](https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_en#change_log)
@@ -86,6 +100,30 @@ Add firmware for flash size 1Mbytes, 2Mbytes, 4Mbytes. +#Build option +####file ./app/include/user_config.h +```c +#define FLASH_512K +// #define FLASH_1M +// #define FLASH_2M +// #define FLASH_4M +... +#define LUA_USE_MODULES +#ifdef LUA_USE_MODULES +#define LUA_USE_MODULES_NODE +#define LUA_USE_MODULES_FILE +#define LUA_USE_MODULES_GPIO +#define LUA_USE_MODULES_WIFI +#define LUA_USE_MODULES_NET +#define LUA_USE_MODULES_PWM +#define LUA_USE_MODULES_I2C +#define LUA_USE_MODULES_TMR +#define LUA_USE_MODULES_ADC +#define LUA_USE_MODULES_UART +#define LUA_USE_MODULES_OW +//#define LUA_USE_MODULES_BIT +#endif /* LUA_USE_MODULES */ +``` #Flash the firmware nodemcu_512k.bin: 0x00000
@@ -227,9 +265,3 @@ braudrate:9600 t = nil package.loaded["ds18b20"]=nil ``` - -#Check this out -Tencent QQ group: 309957875
-[nodemcu wiki](https://github.com/nodemcu/nodemcu-firmware/wiki)
-[nodemcu.com](http://www.nodemcu.com) -[中文bbs](http://bbs.nodemcu.com) diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 00000000..2e2287fd --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,2 @@ +*.output* +!.gitignore diff --git a/app/Makefile b/app/Makefile new file mode 100644 index 00000000..68c3e79e --- /dev/null +++ b/app/Makefile @@ -0,0 +1,166 @@ +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of object file images to be generated () +# GEN_BINS - list of binaries to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +TARGET = eagle +#FLAVOR = release +FLAVOR = debug + +#EXTRA_CCFLAGS += -u + +ifndef PDIR # { +GEN_IMAGES= eagle.app.v6.out +GEN_BINS= eagle.app.v6.bin +SPECIAL_MKTARGETS=$(APP_MKTARGETS) +SUBDIRS= \ + user \ + driver \ + lwip \ + json \ + ssl \ + upgrade \ + platform \ + libc \ + lua \ + smart \ + wofs \ + modules \ + spiffs + +endif # } PDIR + +APPDIR = . +LDDIR = ../ld + +CCFLAGS += -Os + +TARGET_LDFLAGS = \ + -nostdlib \ + -Wl,-EL \ + --longcalls \ + --text-section-literals + +ifeq ($(FLAVOR),debug) + TARGET_LDFLAGS += -g -O2 +endif + +ifeq ($(FLAVOR),release) + TARGET_LDFLAGS += -g -O0 +endif + +LD_FILE = $(LDDIR)/eagle.app.v6.ld + +ifeq ($(APP), 1) + LD_FILE = $(LDDIR)/eagle.app.v6.app1.ld +endif + +ifeq ($(APP), 2) + LD_FILE = $(LDDIR)/eagle.app.v6.app2.ld +endif + +COMPONENTS_eagle.app.v6 = \ + user/libuser.a \ + driver/libdriver.a \ + lwip/liblwip.a \ + json/libjson.a \ + ssl/libssl.a \ + upgrade/libupgrade.a \ + platform/libplatform.a \ + libc/liblibc.a \ + lua/liblua.a \ + smart/smart.a \ + wofs/wofs.a \ + spiffs/spiffs.a \ + modules/libmodules.a + +LINKFLAGS_eagle.app.v6 = \ + -L../lib \ + -nostdlib \ + -T$(LD_FILE) \ + -Wl,--no-check-sections \ + -u call_user_start \ + -Wl,-static \ + -Wl,--start-group \ + -lc \ + -lgcc \ + -lhal \ + -lphy \ + -lpp \ + -lnet80211 \ + -lwpa \ + -lmain \ + -ljson \ + $(DEP_LIBS_eagle.app.v6) \ + -Wl,--end-group + +DEPENDS_eagle.app.v6 = \ + $(LD_FILE) \ + $(LDDIR)/eagle.rom.addr.v6.ld + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# + +#UNIVERSAL_TARGET_DEFINES = \ + +# Other potential configuration flags include: +# -DTXRX_TXBUF_DEBUG +# -DTXRX_RXBUF_DEBUG +# -DWLAN_CONFIG_CCX +CONFIGURATION_DEFINES = -D__ets__ \ + -DICACHE_FLASH \ + -DLWIP_OPEN_SRC \ + -DPBUF_RSV_FOR_WLAN \ + -DEBUF_LWIP + +DEFINES += \ + $(UNIVERSAL_TARGET_DEFINES) \ + $(CONFIGURATION_DEFINES) + +DDEFINES += \ + $(UNIVERSAL_TARGET_DEFINES) \ + $(CONFIGURATION_DEFINES) + + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + +######################################################################### +# +# generate bin file +# + +$(BINODIR)/%.bin: $(IMAGEODIR)/%.out + @mkdir -p $(BINODIR) + $(OBJCOPY) -O binary $< $@ + +.PHONY: FORCE +FORCE: + diff --git a/app/driver/Makefile b/app/driver/Makefile new file mode 100644 index 00000000..ebf7ac5c --- /dev/null +++ b/app/driver/Makefile @@ -0,0 +1,45 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +GEN_LIBS = libdriver.a +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ../platform +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/driver/gpio16.c b/app/driver/gpio16.c new file mode 100644 index 00000000..e0258644 --- /dev/null +++ b/app/driver/gpio16.c @@ -0,0 +1,42 @@ +#include "ets_sys.h" +#include "osapi.h" +#include "driver/gpio16.h" + +void ICACHE_FLASH_ATTR +gpio16_output_conf(void) +{ + WRITE_PERI_REG(PAD_XPD_DCDC_CONF, + (READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xffffffbc) | (uint32)0x1); // mux configuration for XPD_DCDC to output rtc_gpio0 + + WRITE_PERI_REG(RTC_GPIO_CONF, + (READ_PERI_REG(RTC_GPIO_CONF) & (uint32)0xfffffffe) | (uint32)0x0); //mux configuration for out enable + + WRITE_PERI_REG(RTC_GPIO_ENABLE, + (READ_PERI_REG(RTC_GPIO_ENABLE) & (uint32)0xfffffffe) | (uint32)0x1); //out enable +} + +void ICACHE_FLASH_ATTR +gpio16_output_set(uint8 value) +{ + WRITE_PERI_REG(RTC_GPIO_OUT, + (READ_PERI_REG(RTC_GPIO_OUT) & (uint32)0xfffffffe) | (uint32)(value & 1)); +} + +void ICACHE_FLASH_ATTR +gpio16_input_conf(void) +{ + WRITE_PERI_REG(PAD_XPD_DCDC_CONF, + (READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xffffffbc) | (uint32)0x1); // mux configuration for XPD_DCDC and rtc_gpio0 connection + + WRITE_PERI_REG(RTC_GPIO_CONF, + (READ_PERI_REG(RTC_GPIO_CONF) & (uint32)0xfffffffe) | (uint32)0x0); //mux configuration for out enable + + WRITE_PERI_REG(RTC_GPIO_ENABLE, + READ_PERI_REG(RTC_GPIO_ENABLE) & (uint32)0xfffffffe); //out disable +} + +uint8 ICACHE_FLASH_ATTR +gpio16_input_get(void) +{ + return (uint8)(READ_PERI_REG(RTC_GPIO_IN_DATA) & 1); +} diff --git a/app/driver/i2c_master.c b/app/driver/i2c_master.c new file mode 100644 index 00000000..24128eaa --- /dev/null +++ b/app/driver/i2c_master.c @@ -0,0 +1,293 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: i2c_master.c + * + * Description: i2c master API + * + * Modification history: + * 2014/3/12, v1.0 create this file. +*******************************************************************************/ +#include "ets_sys.h" +#include "osapi.h" +#include "gpio.h" + +#include "driver/i2c_master.h" + +#include "pin_map.h" + +LOCAL uint8 m_nLastSDA; +LOCAL uint8 m_nLastSCL; + +LOCAL uint8 pinSDA = 2; +LOCAL uint8 pinSCL = 15; + +/****************************************************************************** + * FunctionName : i2c_master_setDC + * Description : Internal used function - + * set i2c SDA and SCL bit value for half clk cycle + * Parameters : uint8 SDA + * uint8 SCL + * Returns : NONE +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +i2c_master_setDC(uint8 SDA, uint8 SCL) +{ + SDA &= 0x01; + SCL &= 0x01; + m_nLastSDA = SDA; + m_nLastSCL = SCL; + + if ((0 == SDA) && (0 == SCL)) { + I2C_MASTER_SDA_LOW_SCL_LOW(); + } else if ((0 == SDA) && (1 == SCL)) { + I2C_MASTER_SDA_LOW_SCL_HIGH(); + } else if ((1 == SDA) && (0 == SCL)) { + I2C_MASTER_SDA_HIGH_SCL_LOW(); + } else { + I2C_MASTER_SDA_HIGH_SCL_HIGH(); + } +} + +/****************************************************************************** + * FunctionName : i2c_master_getDC + * Description : Internal used function - + * get i2c SDA bit value + * Parameters : NONE + * Returns : uint8 - SDA bit value +*******************************************************************************/ +LOCAL uint8 ICACHE_FLASH_ATTR +i2c_master_getDC(void) +{ + uint8 sda_out; + sda_out = GPIO_INPUT_GET(GPIO_ID_PIN(I2C_MASTER_SDA_GPIO)); + return sda_out; +} + +/****************************************************************************** + * FunctionName : i2c_master_init + * Description : initilize I2C bus to enable i2c operations + * Parameters : NONE + * Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +i2c_master_init(void) +{ + uint8 i; + + i2c_master_setDC(1, 0); + i2c_master_wait(5); + + // when SCL = 0, toggle SDA to clear up + i2c_master_setDC(0, 0) ; + i2c_master_wait(5); + i2c_master_setDC(1, 0) ; + i2c_master_wait(5); + + // set data_cnt to max value + for (i = 0; i < 28; i++) { + i2c_master_setDC(1, 0); + i2c_master_wait(5); // sda 1, scl 0 + i2c_master_setDC(1, 1); + i2c_master_wait(5); // sda 1, scl 1 + } + + // reset all + i2c_master_stop(); + return; +} + +uint8 i2c_master_get_pinSDA(){ + return pinSDA; +} + +uint8 i2c_master_get_pinSCL(){ + return pinSCL; +} + +/****************************************************************************** + * FunctionName : i2c_master_gpio_init + * Description : config SDA and SCL gpio to open-drain output mode, + * mux and gpio num defined in i2c_master.h + * Parameters : NONE + * Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +i2c_master_gpio_init(uint8 sda, uint8 scl) +{ + pinSDA = pin_num[sda]; + pinSCL = pin_num[scl]; + + ETS_GPIO_INTR_DISABLE() ; +// ETS_INTR_LOCK(); + + PIN_FUNC_SELECT(I2C_MASTER_SDA_MUX, I2C_MASTER_SDA_FUNC); + PIN_FUNC_SELECT(I2C_MASTER_SCL_MUX, I2C_MASTER_SCL_FUNC); + + GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_MASTER_SDA_GPIO)), GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_MASTER_SDA_GPIO))) | GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)); //open drain; + GPIO_REG_WRITE(GPIO_ENABLE_ADDRESS, GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | (1 << I2C_MASTER_SDA_GPIO)); + GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_MASTER_SCL_GPIO)), GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_MASTER_SCL_GPIO))) | GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)); //open drain; + GPIO_REG_WRITE(GPIO_ENABLE_ADDRESS, GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | (1 << I2C_MASTER_SCL_GPIO)); + + I2C_MASTER_SDA_HIGH_SCL_HIGH(); + + ETS_GPIO_INTR_ENABLE() ; +// ETS_INTR_UNLOCK(); + + i2c_master_init(); +} + +/****************************************************************************** + * FunctionName : i2c_master_start + * Description : set i2c to send state + * Parameters : NONE + * Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +i2c_master_start(void) +{ + i2c_master_setDC(1, m_nLastSCL); + i2c_master_wait(5); + i2c_master_setDC(1, 1); + i2c_master_wait(5); // sda 1, scl 1 + i2c_master_setDC(0, 1); + i2c_master_wait(5); // sda 0, scl 1 +} + +/****************************************************************************** + * FunctionName : i2c_master_stop + * Description : set i2c to stop sending state + * Parameters : NONE + * Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +i2c_master_stop(void) +{ + i2c_master_wait(5); + + i2c_master_setDC(0, m_nLastSCL); + i2c_master_wait(5); // sda 0 + i2c_master_setDC(0, 1); + i2c_master_wait(5); // sda 0, scl 1 + i2c_master_setDC(1, 1); + i2c_master_wait(5); // sda 1, scl 1 +} + +/****************************************************************************** + * FunctionName : i2c_master_setAck + * Description : set ack to i2c bus as level value + * Parameters : uint8 level - 0 or 1 + * Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +i2c_master_setAck(uint8 level) +{ + i2c_master_setDC(m_nLastSDA, 0); + i2c_master_wait(5); + i2c_master_setDC(level, 0); + i2c_master_wait(5); // sda level, scl 0 + i2c_master_setDC(level, 1); + i2c_master_wait(8); // sda level, scl 1 + i2c_master_setDC(level, 0); + i2c_master_wait(5); // sda level, scl 0 + i2c_master_setDC(1, 0); + i2c_master_wait(5); +} + +/****************************************************************************** + * FunctionName : i2c_master_getAck + * Description : confirm if peer send ack + * Parameters : NONE + * Returns : uint8 - ack value, 0 or 1 +*******************************************************************************/ +uint8 ICACHE_FLASH_ATTR +i2c_master_getAck(void) +{ + uint8 retVal; + i2c_master_setDC(m_nLastSDA, 0); + i2c_master_wait(5); + i2c_master_setDC(1, 0); + i2c_master_wait(5); + i2c_master_setDC(1, 1); + i2c_master_wait(5); + + retVal = i2c_master_getDC(); + i2c_master_wait(5); + i2c_master_setDC(1, 0); + i2c_master_wait(5); + + return retVal; +} + +/****************************************************************************** + * FunctionName : i2c_master_readByte + * Description : read Byte from i2c bus + * Parameters : NONE + * Returns : uint8 - readed value +*******************************************************************************/ +uint8 ICACHE_FLASH_ATTR +i2c_master_readByte(void) +{ + uint8 retVal = 0; + uint8 k, i; + + i2c_master_wait(5); + i2c_master_setDC(m_nLastSDA, 0); + i2c_master_wait(5); // sda 1, scl 0 + + for (i = 0; i < 8; i++) { + i2c_master_wait(5); + i2c_master_setDC(1, 0); + i2c_master_wait(5); // sda 1, scl 0 + i2c_master_setDC(1, 1); + i2c_master_wait(5); // sda 1, scl 1 + + k = i2c_master_getDC(); + i2c_master_wait(5); + + if (i == 7) { + i2c_master_wait(3); //// + } + + k <<= (7 - i); + retVal |= k; + } + + i2c_master_setDC(1, 0); + i2c_master_wait(5); // sda 1, scl 0 + + return retVal; +} + +/****************************************************************************** + * FunctionName : i2c_master_writeByte + * Description : write wrdata value(one byte) into i2c + * Parameters : uint8 wrdata - write value + * Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +i2c_master_writeByte(uint8 wrdata) +{ + uint8 dat; + sint8 i; + + i2c_master_wait(5); + + i2c_master_setDC(m_nLastSDA, 0); + i2c_master_wait(5); + + for (i = 7; i >= 0; i--) { + dat = wrdata >> i; + i2c_master_setDC(dat, 0); + i2c_master_wait(5); + i2c_master_setDC(dat, 1); + i2c_master_wait(5); + + if (i == 0) { + i2c_master_wait(3); //// + } + + i2c_master_setDC(dat, 0); + i2c_master_wait(5); + } +} diff --git a/app/driver/key.c b/app/driver/key.c new file mode 100644 index 00000000..d05c4aca --- /dev/null +++ b/app/driver/key.c @@ -0,0 +1,162 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: key.c + * + * Description: key driver, now can use different gpio and install different function + * + * Modification history: + * 2014/5/1, v1.0 create this file. +*******************************************************************************/ +#include "ets_sys.h" +#include "os_type.h" +#include "osapi.h" +#include "mem.h" +#include "gpio.h" +#include "user_interface.h" + +#include "driver/key.h" + +LOCAL void key_intr_handler(struct keys_param *keys); + +/****************************************************************************** + * FunctionName : key_init_single + * Description : init single key's gpio and register function + * Parameters : uint8 gpio_id - which gpio to use + * uint32 gpio_name - gpio mux name + * uint32 gpio_func - gpio function + * key_function long_press - long press function, needed to install + * key_function short_press - short press function, needed to install + * Returns : single_key_param - single key parameter, needed by key init +*******************************************************************************/ +struct single_key_param *ICACHE_FLASH_ATTR +key_init_single(uint8 gpio_id, uint32 gpio_name, uint8 gpio_func, key_function long_press, key_function short_press) +{ + struct single_key_param *single_key = (struct single_key_param *)os_zalloc(sizeof(struct single_key_param)); + + single_key->gpio_id = gpio_id; + single_key->gpio_name = gpio_name; + single_key->gpio_func = gpio_func; + single_key->long_press = long_press; + single_key->short_press = short_press; + + return single_key; +} + +/****************************************************************************** + * FunctionName : key_init + * Description : init keys + * Parameters : key_param *keys - keys parameter, which inited by key_init_single + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +key_init(struct keys_param *keys) +{ + uint8 i; + + ETS_GPIO_INTR_ATTACH(key_intr_handler, keys); + + ETS_GPIO_INTR_DISABLE(); + + for (i = 0; i < keys->key_num; i++) { + keys->single_key[i]->key_level = 1; + + PIN_FUNC_SELECT(keys->single_key[i]->gpio_name, keys->single_key[i]->gpio_func); + + gpio_output_set(0, 0, 0, GPIO_ID_PIN(keys->single_key[i]->gpio_id)); + + gpio_register_set(GPIO_PIN_ADDR(keys->single_key[i]->gpio_id), GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE) + | GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE) + | GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE)); + + //clear gpio14 status + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(keys->single_key[i]->gpio_id)); + + //enable interrupt + gpio_pin_intr_state_set(GPIO_ID_PIN(keys->single_key[i]->gpio_id), GPIO_PIN_INTR_NEGEDGE); + } + + ETS_GPIO_INTR_ENABLE(); +} + +/****************************************************************************** + * FunctionName : key_5s_cb + * Description : long press 5s timer callback + * Parameters : single_key_param *single_key - single key parameter + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +key_5s_cb(struct single_key_param *single_key) +{ + os_timer_disarm(&single_key->key_5s); + + // low, then restart + if (0 == GPIO_INPUT_GET(GPIO_ID_PIN(single_key->gpio_id))) { + if (single_key->long_press) { + single_key->long_press(); + } + } +} + +/****************************************************************************** + * FunctionName : key_50ms_cb + * Description : 50ms timer callback to check it's a real key push + * Parameters : single_key_param *single_key - single key parameter + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +key_50ms_cb(struct single_key_param *single_key) +{ + os_timer_disarm(&single_key->key_50ms); + + // high, then key is up + if (1 == GPIO_INPUT_GET(GPIO_ID_PIN(single_key->gpio_id))) { + os_timer_disarm(&single_key->key_5s); + single_key->key_level = 1; + gpio_pin_intr_state_set(GPIO_ID_PIN(single_key->gpio_id), GPIO_PIN_INTR_NEGEDGE); + + if (single_key->short_press) { + single_key->short_press(); + } + } else { + gpio_pin_intr_state_set(GPIO_ID_PIN(single_key->gpio_id), GPIO_PIN_INTR_POSEDGE); + } +} + +/****************************************************************************** + * FunctionName : key_intr_handler + * Description : key interrupt handler + * Parameters : key_param *keys - keys parameter, which inited by key_init_single + * Returns : none +*******************************************************************************/ +LOCAL void +key_intr_handler(struct keys_param *keys) +{ + uint8 i; + uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); + + for (i = 0; i < keys->key_num; i++) { + if (gpio_status & BIT(keys->single_key[i]->gpio_id)) { + //disable interrupt + gpio_pin_intr_state_set(GPIO_ID_PIN(keys->single_key[i]->gpio_id), GPIO_PIN_INTR_DISABLE); + + //clear interrupt status + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(keys->single_key[i]->gpio_id)); + + if (keys->single_key[i]->key_level == 1) { + // 5s, restart & enter softap mode + os_timer_disarm(&keys->single_key[i]->key_5s); + os_timer_setfn(&keys->single_key[i]->key_5s, (os_timer_func_t *)key_5s_cb, keys->single_key[i]); + os_timer_arm(&keys->single_key[i]->key_5s, 5000, 0); + keys->single_key[i]->key_level = 0; + gpio_pin_intr_state_set(GPIO_ID_PIN(keys->single_key[i]->gpio_id), GPIO_PIN_INTR_POSEDGE); + } else { + // 50ms, check if this is a real key up + os_timer_disarm(&keys->single_key[i]->key_50ms); + os_timer_setfn(&keys->single_key[i]->key_50ms, (os_timer_func_t *)key_50ms_cb, keys->single_key[i]); + os_timer_arm(&keys->single_key[i]->key_50ms, 50, 0); + } + } + } +} + diff --git a/app/driver/onewire.c b/app/driver/onewire.c new file mode 100644 index 00000000..552f3b67 --- /dev/null +++ b/app/driver/onewire.c @@ -0,0 +1,547 @@ +/* +Adaptation of Paul Stoffregen's One wire library to the NodeMcu + +The latest version of this library may be found at: + http://www.pjrc.com/teensy/td_libs_OneWire.html + +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. + +Much of the code was inspired by Derek Yerger's code, though I don't +think much of that remains. In any event that was.. + (copyleft) 2006 by Derek Yerger - Free to distribute freely. + +The CRC code was excerpted and inspired by the Dallas Semiconductor +sample code bearing this copyright. +//--------------------------------------------------------------------------- +// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved. +// +// 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 DALLAS SEMICONDUCTOR 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. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//-------------------------------------------------------------------------- +*/ + +#include "driver/onewire.h" +#include "platform.h" +#include "osapi.h" + +#define noInterrupts os_intr_lock +#define interrupts os_intr_unlock +#define delayMicroseconds os_delay_us + +#if ONEWIRE_SEARCH +// global search state +static unsigned char ROM_NO[NUM_OW][8]; +static uint8_t LastDiscrepancy[NUM_OW]; +static uint8_t LastFamilyDiscrepancy[NUM_OW]; +static uint8_t LastDeviceFlag[NUM_OW]; +#endif + +void ICACHE_FLASH_ATTR onewire_init(uint8_t pin) +{ + // pinMode(pin, INPUT); + platform_gpio_mode(pin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP); +#if ONEWIRE_SEARCH + onewire_reset_search(pin); +#endif +} + + +// Perform the onewire reset function. We will wait up to 250uS for +// the bus to come high, if it doesn't then it is broken or shorted +// and we return a 0; +// +// Returns 1 if a device asserted a presence pulse, 0 otherwise. +// +uint8_t ICACHE_FLASH_ATTR onewire_reset(uint8_t pin) +{ + uint8_t r; + uint8_t retries = 125; + + noInterrupts(); + DIRECT_MODE_INPUT(pin); + interrupts(); + // wait until the wire is high... just in case + do { + if (--retries == 0) return 0; + delayMicroseconds(2); + } while ( !DIRECT_READ(pin)); + + noInterrupts(); + DIRECT_WRITE_LOW(pin); + DIRECT_MODE_OUTPUT(pin); // drive output low + interrupts(); + delayMicroseconds(480); + noInterrupts(); + DIRECT_MODE_INPUT(pin); // allow it to float + delayMicroseconds(70); + r = !DIRECT_READ(pin); + interrupts(); + delayMicroseconds(410); + return r; +} + +// +// Write a bit. Port and bit is used to cut lookup time and provide +// more certain timing. +// +static void ICACHE_FLASH_ATTR onewire_write_bit(uint8_t pin, uint8_t v) +{ + if (v & 1) { + noInterrupts(); + DIRECT_WRITE_LOW(pin); + DIRECT_MODE_OUTPUT(pin); // drive output low + delayMicroseconds(10); + DIRECT_WRITE_HIGH(pin); // drive output high + interrupts(); + delayMicroseconds(55); + } else { + noInterrupts(); + DIRECT_WRITE_LOW(pin); + DIRECT_MODE_OUTPUT(pin); // drive output low + delayMicroseconds(65); + DIRECT_WRITE_HIGH(pin); // drive output high + interrupts(); + delayMicroseconds(5); + } +} + +// +// Read a bit. Port and bit is used to cut lookup time and provide +// more certain timing. +// +static uint8_t ICACHE_FLASH_ATTR onewire_read_bit(uint8_t pin) +{ + uint8_t r; + + noInterrupts(); + DIRECT_MODE_OUTPUT(pin); + DIRECT_WRITE_LOW(pin); + delayMicroseconds(3); + DIRECT_MODE_INPUT(pin); // let pin float, pull up will raise + delayMicroseconds(10); + r = DIRECT_READ(pin); + interrupts(); + delayMicroseconds(53); + return r; +} + +// +// Write a byte. The writing code uses the active drivers to raise the +// pin high, if you need power after the write (e.g. DS18S20 in +// parasite power mode) then set 'power' to 1, otherwise the pin will +// go tri-state at the end of the write to avoid heating in a short or +// other mishap. +// +void ICACHE_FLASH_ATTR onewire_write(uint8_t pin, uint8_t v, uint8_t power /* = 0 */) { + uint8_t bitMask; + + for (bitMask = 0x01; bitMask; bitMask <<= 1) { + onewire_write_bit(pin, (bitMask & v)?1:0); + } + if ( !power) { + noInterrupts(); + DIRECT_MODE_INPUT(pin); + DIRECT_WRITE_LOW(pin); + interrupts(); + } +} + +void ICACHE_FLASH_ATTR onewire_write_bytes(uint8_t pin, const uint8_t *buf, uint16_t count, bool power /* = 0 */) { + uint16_t i; + for (i = 0 ; i < count ; i++) + onewire_write(pin, buf[i], 0); + if (!power) { + noInterrupts(); + DIRECT_MODE_INPUT(pin); + DIRECT_WRITE_LOW(pin); + interrupts(); + } +} + +// +// Read a byte +// +uint8_t ICACHE_FLASH_ATTR onewire_read(uint8_t pin) { + uint8_t bitMask; + uint8_t r = 0; + + for (bitMask = 0x01; bitMask; bitMask <<= 1) { + if (onewire_read_bit(pin)) r |= bitMask; + } + return r; +} + +void ICACHE_FLASH_ATTR onewire_read_bytes(uint8_t pin, uint8_t *buf, uint16_t count) { + uint16_t i; + for (i = 0 ; i < count ; i++) + buf[i] = onewire_read(pin); +} + +// +// Do a ROM select +// +void ICACHE_FLASH_ATTR onewire_select(uint8_t pin, const uint8_t rom[8]) +{ + uint8_t i; + + onewire_write(pin, 0x55, 0); // Choose ROM + + for (i = 0; i < 8; i++) onewire_write(pin, rom[i], 0); +} + +// +// Do a ROM skip +// +void ICACHE_FLASH_ATTR onewire_skip(uint8_t pin) +{ + onewire_write(pin, 0xCC, 0); // Skip ROM +} + +void ICACHE_FLASH_ATTR onewire_depower(uint8_t pin) +{ + noInterrupts(); + DIRECT_MODE_INPUT(pin); + interrupts(); +} + +#if ONEWIRE_SEARCH + +// +// You need to use this function to start a search again from the beginning. +// You do not need to do it for the first search, though you could. +// +void ICACHE_FLASH_ATTR onewire_reset_search(uint8_t pin) +{ + // reset the search state + LastDiscrepancy[pin] = 0; + LastDeviceFlag[pin] = FALSE; + LastFamilyDiscrepancy[pin] = 0; + int i; + for(i = 7; ; i--) { + ROM_NO[pin][i] = 0; + if ( i == 0) break; + } +} + +// Setup the search to find the device type 'family_code' on the next call +// to search(*newAddr) if it is present. +// +void ICACHE_FLASH_ATTR onewire_target_search(uint8_t pin, uint8_t family_code) +{ + // set the search state to find SearchFamily type devices + ROM_NO[pin][0] = family_code; + uint8_t i; + for (i = 1; i < 8; i++) + ROM_NO[pin][i] = 0; + LastDiscrepancy[pin] = 64; + LastFamilyDiscrepancy[pin] = 0; + LastDeviceFlag[pin] = FALSE; +} + +// +// Perform a search. If this function returns a '1' then it has +// enumerated the next device and you may retrieve the ROM from the +// OneWire::address variable. If there are no devices, no further +// devices, or something horrible happens in the middle of the +// enumeration then a 0 is returned. If a new device is found then +// its address is copied to newAddr. Use OneWire::reset_search() to +// start over. +// +// --- Replaced by the one from the Dallas Semiconductor web site --- +//-------------------------------------------------------------------------- +// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing +// search state. +// Return TRUE : device found, ROM number in ROM_NO buffer +// FALSE : device not found, end of search +// +uint8_t ICACHE_FLASH_ATTR onewire_search(uint8_t pin, uint8_t *newAddr) +{ + uint8_t id_bit_number; + uint8_t last_zero, rom_byte_number, search_result; + uint8_t id_bit, cmp_id_bit; + + unsigned char rom_byte_mask, search_direction; + + // initialize for search + id_bit_number = 1; + last_zero = 0; + rom_byte_number = 0; + rom_byte_mask = 1; + search_result = 0; + + // if the last call was not the last one + if (!LastDeviceFlag[pin]) + { + // 1-Wire reset + if (!onewire_reset(pin)) + { + // reset the search + LastDiscrepancy[pin] = 0; + LastDeviceFlag[pin] = FALSE; + LastFamilyDiscrepancy[pin] = 0; + return FALSE; + } + + // issue the search command + onewire_write(pin, 0xF0, 0); + + // loop to do the search + do + { + // read a bit and its complement + id_bit = onewire_read_bit(pin); + cmp_id_bit = onewire_read_bit(pin); + + // check for no devices on 1-wire + if ((id_bit == 1) && (cmp_id_bit == 1)) + break; + else + { + // all devices coupled have 0 or 1 + if (id_bit != cmp_id_bit) + search_direction = id_bit; // bit write value for search + else + { + // if this discrepancy if before the Last Discrepancy + // on a previous next then pick the same as last time + if (id_bit_number < LastDiscrepancy[pin]) + search_direction = ((ROM_NO[pin][rom_byte_number] & rom_byte_mask) > 0); + else + // if equal to last pick 1, if not then pick 0 + search_direction = (id_bit_number == LastDiscrepancy[pin]); + + // if 0 was picked then record its position in LastZero + if (search_direction == 0) + { + last_zero = id_bit_number; + + // check for Last discrepancy in family + if (last_zero < 9) + LastFamilyDiscrepancy[pin] = last_zero; + } + } + + // set or clear the bit in the ROM byte rom_byte_number + // with mask rom_byte_mask + if (search_direction == 1) + ROM_NO[pin][rom_byte_number] |= rom_byte_mask; + else + ROM_NO[pin][rom_byte_number] &= ~rom_byte_mask; + + // serial number search direction write bit + onewire_write_bit(pin, search_direction); + + // increment the byte counter id_bit_number + // and shift the mask rom_byte_mask + id_bit_number++; + rom_byte_mask <<= 1; + + // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask + if (rom_byte_mask == 0) + { + rom_byte_number++; + rom_byte_mask = 1; + } + } + } + while(rom_byte_number < 8); // loop until through all ROM bytes 0-7 + + // if the search was successful then + if (!(id_bit_number < 65)) + { + // search successful so set LastDiscrepancy,LastDeviceFlag,search_result + LastDiscrepancy[pin] = last_zero; + + // check for last device + if (LastDiscrepancy[pin] == 0) + LastDeviceFlag[pin] = TRUE; + + search_result = TRUE; + } + } + + // if no device found then reset counters so next 'search' will be like a first + if (!search_result || !ROM_NO[pin][0]) + { + LastDiscrepancy[pin] = 0; + LastDeviceFlag[pin] = FALSE; + LastFamilyDiscrepancy[pin] = 0; + search_result = FALSE; + } + int i; + for (i = 0; i < 8; i++) newAddr[i] = ROM_NO[pin][i]; + return search_result; + } + +#endif + + +#if ONEWIRE_CRC +// The 1-Wire CRC scheme is described in Maxim Application Note 27: +// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" +// + +#if ONEWIRE_CRC8_TABLE +// This table comes from Dallas sample code where it is freely reusable, +// though Copyright (C) 2000 Dallas Semiconductor Corporation +static const uint8_t dscrc_table[] = { + 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65, + 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220, + 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98, + 190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255, + 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7, + 219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154, + 101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36, + 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185, + 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205, + 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80, + 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238, + 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115, + 202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139, + 87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22, + 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168, + 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53}; + +#ifndef pgm_read_byte +#define pgm_read_byte(addr) (*(const uint8_t *)(addr)) +#endif + +// +// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM +// and the registers. (note: this might better be done without to +// table, it would probably be smaller and certainly fast enough +// compared to all those delayMicrosecond() calls. But I got +// confused, so I use this table from the examples.) +// +uint8_t ICACHE_FLASH_ATTR onewire_crc8(const uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) { + crc = pgm_read_byte(dscrc_table + (crc ^ *addr++)); + } + return crc; +} +#else +// +// Compute a Dallas Semiconductor 8 bit CRC directly. +// this is much slower, but much smaller, than the lookup table. +// +uint8_t ICACHE_FLASH_ATTR onewire_crc8(const uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) { + uint8_t inbyte = *addr++; + for (uint8_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) crc ^= 0x8C; + inbyte >>= 1; + } + } + return crc; +} +#endif + +#if ONEWIRE_CRC16 +// Compute the 1-Wire CRC16 and compare it against the received CRC. +// Example usage (reading a DS2408): + // // Put everything in a buffer so we can compute the CRC easily. +// uint8_t buf[13]; +// buf[0] = 0xF0; // Read PIO Registers +// buf[1] = 0x88; // LSB address +// buf[2] = 0x00; // MSB address +// WriteBytes(net, buf, 3); // Write 3 cmd bytes +// ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 +// if (!CheckCRC16(buf, 11, &buf[11])) { +// // Handle error. +// } +// +// @param input - Array of bytes to checksum. +// @param len - How many bytes to use. +// @param inverted_crc - The two CRC16 bytes in the received data. +// This should just point into the received data, +// *not* at a 16-bit integer. +// @param crc - The crc starting value (optional) +// @return True, iff the CRC matches. +bool ICACHE_FLASH_ATTR onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc) +{ + crc = ~onewire_crc16(input, len, crc); + return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1]; +} + +// Compute a Dallas Semiconductor 16 bit CRC. This is required to check +// the integrity of data received from many 1-Wire devices. Note that the +// CRC computed here is *not* what you'll get from the 1-Wire network, +// for two reasons: +// 1) The CRC is transmitted bitwise inverted. +// 2) Depending on the endian-ness of your processor, the binary +// representation of the two-byte return value may have a different +// byte order than the two bytes you get from 1-Wire. +// @param input - Array of bytes to checksum. +// @param len - How many bytes to use. +// @param crc - The crc starting value (optional) +// @return The CRC16, as defined by Dallas Semiconductor. +uint16_t ICACHE_FLASH_ATTR onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc) +{ + static const uint8_t oddparity[16] = + { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; + + uint16_t i; + for (i = 0 ; i < len ; i++) { + // Even though we're just copying a byte from the input, + // we'll be doing 16-bit computation with it. + uint16_t cdata = input[i]; + cdata = (cdata ^ crc) & 0xff; + crc >>= 8; + + if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4]) + crc ^= 0xC001; + + cdata <<= 6; + crc ^= cdata; + cdata <<= 1; + crc ^= cdata; + } + return crc; +} +#endif + +#endif diff --git a/app/driver/pwm.c b/app/driver/pwm.c new file mode 100644 index 00000000..e292ff63 --- /dev/null +++ b/app/driver/pwm.c @@ -0,0 +1,446 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: pwm.c + * + * Description: pwm driver + * + * Modification history: + * 2014/5/1, v1.0 create this file. +*******************************************************************************/ +#include "platform.h" + +#include "ets_sys.h" +#include "os_type.h" +#include "osapi.h" +#include "gpio.h" + +#include "user_interface.h" +#include "driver/pwm.h" + +// #define PWM_DBG os_printf +#define PWM_DBG + +LOCAL struct pwm_single_param pwm_single_toggle[2][PWM_CHANNEL + 1]; +LOCAL struct pwm_single_param *pwm_single; + +LOCAL struct pwm_param pwm; + +// LOCAL uint8 pwm_out_io_num[PWM_CHANNEL] = {PWM_0_OUT_IO_NUM, PWM_1_OUT_IO_NUM, PWM_2_OUT_IO_NUM}; +LOCAL int8 pwm_out_io_num[PWM_CHANNEL] = {-1, -1, -1, -1, -1, -1}; + +LOCAL uint8 pwm_channel_toggle[2]; +LOCAL uint8 *pwm_channel; + +LOCAL uint8 pwm_toggle = 1; +LOCAL uint8 pwm_timer_down = 1; + +LOCAL uint8 pwm_current_channel = 0; + +LOCAL uint16 pwm_gpio = 0; + +LOCAL uint8 pwm_channel_num = 0; + +//XXX: 0xffffffff/(80000000/16)=35A +#define US_TO_RTC_TIMER_TICKS(t) \ + ((t) ? \ + (((t) > 0x35A) ? \ + (((t)>>2) * ((APB_CLK_FREQ>>4)/250000) + ((t)&0x3) * ((APB_CLK_FREQ>>4)/1000000)) : \ + (((t) *(APB_CLK_FREQ>>4)) / 1000000)) : \ + 0) + +//FRC1 +#define FRC1_ENABLE_TIMER BIT7 + +typedef enum { + DIVDED_BY_1 = 0, + DIVDED_BY_16 = 4, + DIVDED_BY_256 = 8, +} TIMER_PREDIVED_MODE; + +typedef enum { + TM_LEVEL_INT = 1, + TM_EDGE_INT = 0, +} TIMER_INT_MODE; + +LOCAL void ICACHE_FLASH_ATTR +pwm_insert_sort(struct pwm_single_param pwm[], uint8 n) +{ + uint8 i; + + for (i = 1; i < n; i++) { + if (pwm[i].h_time < pwm[i - 1].h_time) { + int8 j = i - 1; + struct pwm_single_param tmp; + + os_memcpy(&tmp, &pwm[i], sizeof(struct pwm_single_param)); + os_memcpy(&pwm[i], &pwm[i - 1], sizeof(struct pwm_single_param)); + + while (tmp.h_time < pwm[j].h_time) { + os_memcpy(&pwm[j + 1], &pwm[j], sizeof(struct pwm_single_param)); + j--; + if (j < 0) { + break; + } + } + + os_memcpy(&pwm[j + 1], &tmp, sizeof(struct pwm_single_param)); + } + } +} + +LOCAL volatile uint8 critical = 0; + +#define LOCK_PWM(c) do { \ + while( (c)==1 ); \ + (c) = 1; \ +} while (0) + +#define UNLOCK_PWM(c) do { \ + (c) = 0; \ +} while (0) + +void ICACHE_FLASH_ATTR +pwm_start(void) +{ + uint8 i, j; + PWM_DBG("--Function pwm_start() is called\n"); + PWM_DBG("pwm_gpio:%x,pwm_channel_num:%d\n",pwm_gpio,pwm_channel_num); + PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]); + PWM_DBG("pwm.period:%d,pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.period,pwm.duty[0],pwm.duty[1],pwm.duty[2]); + + LOCK_PWM(critical); // enter critical + + struct pwm_single_param *local_single = pwm_single_toggle[pwm_toggle ^ 0x01]; + uint8 *local_channel = &pwm_channel_toggle[pwm_toggle ^ 0x01]; + + // step 1: init PWM_CHANNEL+1 channels param + for (i = 0; i < pwm_channel_num; i++) { + uint32 us = pwm.period * pwm.duty[i] / PWM_DEPTH; + local_single[i].h_time = US_TO_RTC_TIMER_TICKS(us); + PWM_DBG("i:%d us:%d ht:%d\n",i,us,local_single[i].h_time); + local_single[i].gpio_set = 0; + local_single[i].gpio_clear = 1 << pin_num[pwm_out_io_num[i]]; + } + + local_single[pwm_channel_num].h_time = US_TO_RTC_TIMER_TICKS(pwm.period); + local_single[pwm_channel_num].gpio_set = pwm_gpio; + local_single[pwm_channel_num].gpio_clear = 0; + PWM_DBG("i:%d period:%d ht:%d\n",pwm_channel_num,pwm.period,local_single[pwm_channel_num].h_time); + // step 2: sort, small to big + pwm_insert_sort(local_single, pwm_channel_num + 1); + + *local_channel = pwm_channel_num + 1; + PWM_DBG("1channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time); + // step 3: combine same duty channels + for (i = pwm_channel_num; i > 0; i--) { + if (local_single[i].h_time == local_single[i - 1].h_time) { + local_single[i - 1].gpio_set |= local_single[i].gpio_set; + local_single[i - 1].gpio_clear |= local_single[i].gpio_clear; + + for (j = i + 1; j < *local_channel; j++) { + os_memcpy(&local_single[j - 1], &local_single[j], sizeof(struct pwm_single_param)); + } + + (*local_channel)--; + } + } + PWM_DBG("2channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time); + // step 4: cacl delt time + for (i = *local_channel - 1; i > 0; i--) { + local_single[i].h_time -= local_single[i - 1].h_time; + } + + // step 5: last channel needs to clean + local_single[*local_channel-1].gpio_clear = 0; + + // step 6: if first channel duty is 0, remove it + if (local_single[0].h_time == 0) { + local_single[*local_channel - 1].gpio_set &= ~local_single[0].gpio_clear; + local_single[*local_channel - 1].gpio_clear |= local_single[0].gpio_clear; + + for (i = 1; i < *local_channel; i++) { + os_memcpy(&local_single[i - 1], &local_single[i], sizeof(struct pwm_single_param)); + } + + (*local_channel)--; + } + + // if timer is down, need to set gpio and start timer + if (pwm_timer_down == 1) { + pwm_channel = local_channel; + pwm_single = local_single; + // start + gpio_output_set(local_single[0].gpio_set, local_single[0].gpio_clear, pwm_gpio, 0); + + // yeah, if all channels' duty is 0 or 255, don't need to start timer, otherwise start... + if (*local_channel != 1) { + pwm_timer_down = 0; + RTC_REG_WRITE(FRC1_LOAD_ADDRESS, local_single[0].h_time); + } + } + + if (pwm_toggle == 1) { + pwm_toggle = 0; + } else { + pwm_toggle = 1; + } + + UNLOCK_PWM(critical); // leave critical + PWM_DBG("3channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time); +} + +/****************************************************************************** + * FunctionName : pwm_set_duty + * Description : set each channel's duty params + * Parameters : uint8 duty : 0 ~ PWM_DEPTH + * uint8 channel : channel index + * Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +pwm_set_duty(uint16 duty, uint8 channel) +{ + uint8 i; + for(i=0;i= PWM_DEPTH) { + pwm.duty[channel] = PWM_DEPTH; + } else { + pwm.duty[channel] = duty; + } + UNLOCK_PWM(critical); // leave critical +} + +/****************************************************************************** + * FunctionName : pwm_set_freq + * Description : set pwm frequency + * Parameters : uint16 freq : 100hz typically + * Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +pwm_set_freq(uint16 freq, uint8 channel) +{ + LOCK_PWM(critical); // enter critical + if (freq > PWM_FREQ_MAX) { + pwm.freq = PWM_FREQ_MAX; + } else if (freq < 1) { + pwm.freq = 1; + } else { + pwm.freq = freq; + } + + pwm.period = PWM_1S / pwm.freq; + UNLOCK_PWM(critical); // leave critical +} + +/****************************************************************************** + * FunctionName : pwm_set_freq_duty + * Description : set pwm frequency and each channel's duty + * Parameters : uint16 freq : 100hz typically + * uint16 *duty : each channel's duty + * Returns : NONE +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +pwm_set_freq_duty(uint16 freq, uint16 *duty) +{ + uint8 i; + + pwm_set_freq(freq, 0); + + for (i = 0; i < PWM_CHANNEL; i++) { + // pwm_set_duty(duty[i], i); + if(pwm_out_io_num[i] != -1) + pwm_set_duty(duty[i], pwm_out_io_num[i]); + } +} + +/****************************************************************************** + * FunctionName : pwm_get_duty + * Description : get duty of each channel + * Parameters : uint8 channel : channel index + * Returns : NONE +*******************************************************************************/ +uint16 ICACHE_FLASH_ATTR +pwm_get_duty(uint8 channel) +{ + uint8 i; + for(i=0;i= (*pwm_channel - 1)) { // *pwm_channel may change outside + pwm_single = pwm_single_toggle[local_toggle]; + pwm_channel = &pwm_channel_toggle[local_toggle]; + + gpio_output_set(pwm_single[*pwm_channel - 1].gpio_set, + pwm_single[*pwm_channel - 1].gpio_clear, + pwm_gpio, + 0); + + pwm_current_channel = 0; + + if (*pwm_channel != 1) { + RTC_REG_WRITE(FRC1_LOAD_ADDRESS, pwm_single[pwm_current_channel].h_time); + } else { + pwm_timer_down = 1; + } + } else { + gpio_output_set(pwm_single[pwm_current_channel].gpio_set, + pwm_single[pwm_current_channel].gpio_clear, + pwm_gpio, 0); + + pwm_current_channel++; + RTC_REG_WRITE(FRC1_LOAD_ADDRESS, pwm_single[pwm_current_channel].h_time); + } +} + +/****************************************************************************** + * FunctionName : pwm_init + * Description : pwm gpio, params and timer initialization + * Parameters : uint16 freq : pwm freq param + * uint16 *duty : each channel's duty + * Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +pwm_init(uint16 freq, uint16 *duty) +{ + uint8 i; + + ETS_FRC_TIMER1_INTR_ATTACH(pwm_tim1_intr_handler, NULL); + TM1_EDGE_INT_ENABLE(); + ETS_FRC1_INTR_ENABLE(); + + RTC_CLR_REG_MASK(FRC1_INT_ADDRESS, FRC1_INT_CLR_MASK); + RTC_REG_WRITE(FRC1_CTRL_ADDRESS, //FRC2_AUTO_RELOAD| + DIVDED_BY_16 + | FRC1_ENABLE_TIMER + | TM_EDGE_INT); + RTC_REG_WRITE(FRC1_LOAD_ADDRESS, 0); + + // PIN_FUNC_SELECT(PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC); + // PIN_FUNC_SELECT(PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC); + // PIN_FUNC_SELECT(PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC); + // GPIO_OUTPUT_SET(GPIO_ID_PIN(PWM_0_OUT_IO_NUM), 0); + // GPIO_OUTPUT_SET(GPIO_ID_PIN(PWM_1_OUT_IO_NUM), 0); + // GPIO_OUTPUT_SET(GPIO_ID_PIN(PWM_2_OUT_IO_NUM), 0); + + for (i = 0; i < PWM_CHANNEL; i++) { + // pwm_gpio |= (1 << pwm_out_io_num[i]); + pwm_gpio = 0; + pwm.duty[0] = 0; + } + + pwm_set_freq(500, 0); + // pwm_set_freq_duty(freq, duty); + + pwm_start(); +} + +bool ICACHE_FLASH_ATTR +pwm_add(uint8 channel){ + PWM_DBG("--Function pwm_add() is called. channel:%d\n", channel); + PWM_DBG("pwm_gpio:%x,pwm_channel_num:%d\n",pwm_gpio,pwm_channel_num); + PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]); + PWM_DBG("pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.duty[0],pwm.duty[1],pwm.duty[2]); + uint8 i; + for(i=0;ipWritePos == pRxBuff->pReadPos){ // empty + return 0; + } + // ETS_UART_INTR_DISABLE(); + ETS_INTR_LOCK(); + c = (char)*(pRxBuff->pReadPos); + if (pRxBuff->pReadPos == (pRxBuff->pRcvMsgBuff + RX_BUFF_SIZE)) { + pRxBuff->pReadPos = pRxBuff->pRcvMsgBuff ; + } else { + pRxBuff->pReadPos++; + } + // ETS_UART_INTR_ENABLE(); + ETS_INTR_UNLOCK(); + return c; +} + +#if 0 +int ICACHE_FLASH_ATTR readline4lua(const char *prompt, char *buffer, int length){ + char ch; + int line_position; + +start: + /* show prompt */ + uart0_sendStr(prompt); + + line_position = 0; + os_memset(buffer, 0, length); + while (1) + { + while ((ch = uart_getc()) != 0) + { + /* handle CR key */ + if (ch == '\r') + { + char next; + if ((next = uart_getc()) != 0) + ch = next; + } + /* backspace key */ + else if (ch == 0x7f || ch == 0x08) + { + if (line_position > 0) + { + uart_putc(0x08); + uart_putc(' '); + uart_putc(0x08); + line_position--; + } + buffer[line_position] = 0; + continue; + } + /* EOF(ctrl+d) */ + else if (ch == 0x04) + { + if (line_position == 0) + /* No input which makes lua interpreter close */ + return 0; + else + continue; + } + + /* end of line */ + if (ch == '\r' || ch == '\n') + { + buffer[line_position] = 0; + uart_putc('\n'); + if (line_position == 0) + { + /* Get a empty line, then go to get a new line */ + goto start; + } + else + { + return line_position; + } + } + + /* other control character or not an acsii character */ + if (ch < 0x20 || ch >= 0x80) + { + continue; + } + + /* echo */ + uart_putc(ch); + buffer[line_position] = ch; + ch = 0; + line_position++; + + /* it's a large line, discard it */ + if (line_position >= length) + line_position = 0; + } + } +} +#endif diff --git a/app/driver/spi.c b/app/driver/spi.c new file mode 100644 index 00000000..a45b2d64 --- /dev/null +++ b/app/driver/spi.c @@ -0,0 +1,494 @@ +#include "driver/spi.h" + + +/****************************************************************************** + * FunctionName : spi_lcd_mode_init + * Description : SPI master initial function for driving LCD TM035PDZV36 + * Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid +*******************************************************************************/ +void spi_lcd_mode_init(uint8 spi_no) +{ + uint32 regvalue; + if(spi_no>1) return; //handle invalid input number + //bit9 of PERIPHS_IO_MUX should be cleared when HSPI clock doesn't equal CPU clock + //bit8 of PERIPHS_IO_MUX should be cleared when SPI clock doesn't equal CPU clock + if(spi_no==SPI){ + WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005); //clear bit9,and bit8 + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);//configure io to spi mode + }else if(spi_no==HSPI){ + WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); //clear bit9 + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure io to spi mode + } + + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD|SPI_USR_COMMAND); + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE); + // SPI clock=CPU clock/8 + WRITE_PERI_REG(SPI_CLOCK(spi_no), + ((1&SPI_CLKDIV_PRE)<1) return; //handle invalid input number + + if(high_bit) bytetemp=(low_8bit>>1)|0x80; + else bytetemp=(low_8bit>>1)&0x7f; + + regvalue= ((8&SPI_USR_COMMAND_BITLEN)<1) return; //handle invalid input number + + + if(spi_no==SPI){ + WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);//configure io to spi mode + } + else if(spi_no==HSPI){ + WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure io to spi mode + } + + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD|SPI_USR_COMMAND|SPI_USR_MOSI); + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE); + + //clear Daul or Quad lines transmission mode + CLEAR_PERI_REG_MASK(SPI_CTRL(spi_no), SPI_QIO_MODE|SPI_DIO_MODE|SPI_DOUT_MODE|SPI_QOUT_MODE); + + WRITE_PERI_REG(SPI_CLOCK(spi_no), + ((3&SPI_CLKCNT_N)<1) return; //handle invalid input number + + while(READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR); + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI|SPI_USR_MISO); + + //SPI_FLASH_USER2 bit28-31 is cmd length,cmd bit length is value(0-15)+1, + // bit15-0 is cmd value. + WRITE_PERI_REG(SPI_USER2(spi_no), + ((7&SPI_USR_COMMAND_BITLEN)<1) return; //handle invalid input number + + while(READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR); + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI); + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO|SPI_USR_ADDR|SPI_USR_DUMMY); + + //SPI_FLASH_USER2 bit28-31 is cmd length,cmd bit length is value(0-15)+1, + // bit15-0 is cmd value. + //0x70000000 is for 8bits cmd, 0x04 is eps8266 slave write cmd value + WRITE_PERI_REG(SPI_USER2(spi_no), + ((7&SPI_USR_COMMAND_BITLEN)<1) return; //handle invalid input number + + while(READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR); + + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO); + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI|SPI_USR_ADDR|SPI_USR_DUMMY); + //SPI_FLASH_USER2 bit28-31 is cmd length,cmd bit length is value(0-15)+1, + // bit15-0 is cmd value. + //0x70000000 is for 8bits cmd, 0x06 is eps8266 slave read cmd value + WRITE_PERI_REG(SPI_USER2(spi_no), + ((7&SPI_USR_COMMAND_BITLEN)<1) + return; //handle invalid input number + + //clear bit9,bit8 of reg PERIPHS_IO_MUX + //bit9 should be cleared when HSPI clock doesn't equal CPU clock + //bit8 should be cleared when SPI clock doesn't equal CPU clock + ////WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); //clear bit9//TEST + if(spi_no==SPI){ + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);//configure io to spi mode + }else if(spi_no==HSPI){ + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure io to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure io to spi mode + } + + //regvalue=READ_PERI_REG(SPI_FLASH_SLAVE(spi_no)); + //slave mode,slave use buffers which are register "SPI_FLASH_C0~C15", enable trans done isr + //set bit 30 bit 29 bit9,bit9 is trans done isr mask + SET_PERI_REG_MASK( SPI_SLAVE(spi_no), + SPI_SLAVE_MODE|SPI_SLV_WR_RD_BUF_EN| + SPI_SLV_WR_BUF_DONE_EN|SPI_SLV_RD_BUF_DONE_EN| + SPI_SLV_WR_STA_DONE_EN|SPI_SLV_RD_STA_DONE_EN| + SPI_TRANS_DONE_EN); + //disable general trans intr + //CLEAR_PERI_REG_MASK(SPI_SLAVE(spi_no),SPI_TRANS_DONE_EN); + + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE);//disable flash operation mode + SET_PERI_REG_MASK(SPI_USER(spi_no),SPI_USR_MISO_HIGHPART);//SLAVE SEND DATA BUFFER IN C8-C15 + + +//////**************RUN WHEN SLAVE RECIEVE*******************/////// + //tow lines below is to configure spi timing. + SET_PERI_REG_MASK(SPI_CTRL2(spi_no),(0x2&SPI_MOSI_DELAY_NUM)<>8)&0xff; + spi_data[(idx<<2)+2] = (recv_data>>16)&0xff; + spi_data[(idx<<2)+3] = (recv_data>>24)&0xff; + idx++; + } + //add system_os_post here + GPIO_OUTPUT_SET(0, 1); + } + if(regvalue&SPI_SLV_RD_BUF_DONE){ + //it is necessary to call GPIO_OUTPUT_SET(2, 1), when new data is preped in SPI_W8-15 and needs to be sended. + GPIO_OUTPUT_SET(2, 0); + //add system_os_post here + //system_os_post(USER_TASK_PRIO_1,WR_RD,regvalue); + + } + + }else if(READ_PERI_REG(0x3ff00020)&BIT9){ //bit7 is for i2s isr, + + } +} + + +#ifdef SPI_SLAVE_DEBUG + +void ICACHE_FLASH_ATTR + set_miso_data() +{ + if(GPIO_INPUT_GET(2)==0){ + WRITE_PERI_REG(SPI_W8(HSPI),0x05040302); + WRITE_PERI_REG(SPI_W9(HSPI),0x09080706); + WRITE_PERI_REG(SPI_W10(HSPI),0x0d0c0b0a); + WRITE_PERI_REG(SPI_W11(HSPI),0x11100f0e); + + WRITE_PERI_REG(SPI_W12(HSPI),0x15141312); + WRITE_PERI_REG(SPI_W13(HSPI),0x19181716); + WRITE_PERI_REG(SPI_W14(HSPI),0x1d1c1b1a); + WRITE_PERI_REG(SPI_W15(HSPI),0x21201f1e); + GPIO_OUTPUT_SET(2, 1); + } +} + + + +void ICACHE_FLASH_ATTR + disp_spi_data() +{ + uint8 i = 0; + for(i=0;i<32;i++){ + os_printf("data %d : 0x%02x\n\r",i,spi_data[i]); + } + //os_printf("d31:0x%02x\n\r",spi_data[31]); +} + + +void ICACHE_FLASH_ATTR + spi_task(os_event_t *e) +{ + uint8 data; + switch(e->sig){ + case MOSI: + disp_spi_data(); + break; + case STATUS_R_IN_WR : + os_printf("SR ERR in WRPR,Reg:%08x \n",e->par); + break; + case STATUS_W: + os_printf("SW ERR,Reg:%08x\n",e->par); + break; + case TR_DONE_ALONE: + os_printf("TD ALO ERR,Reg:%08x\n",e->par); + break; + case WR_RD: + os_printf("WR&RD ERR,Reg:%08x\n",e->par); + break; + case DATA_ERROR: + os_printf("Data ERR,Reg:%08x\n",e->par); + break; + case STATUS_R_IN_RD : + os_printf("SR ERR in RDPR,Reg:%08x\n",e->par); + break; + default: + break; + } +} + +void ICACHE_FLASH_ATTR + spi_task_init(void) +{ + spiQueue = (os_event_t*)os_malloc(sizeof(os_event_t)*SPI_QUEUE_LEN); + system_os_task(spi_task,USER_TASK_PRIO_1,spiQueue,SPI_QUEUE_LEN); +} + +os_timer_t spi_timer_test; + +void ICACHE_FLASH_ATTR + spi_test_init() +{ + os_printf("spi init\n\r"); + spi_slave_init(HSPI); + os_printf("gpio init\n\r"); + gpio_init(); + os_printf("spi task init \n\r"); + spi_task_init(); +#ifdef SPI_MISO + os_printf("spi miso init\n\r"); + set_miso_data(); +#endif + + //os_timer_disarm(&spi_timer_test); + //os_timer_setfn(&spi_timer_test, (os_timer_func_t *)set_miso_data, NULL);//wjl + //os_timer_arm(&spi_timer_test,50,1); +} + +#endif + + diff --git a/app/driver/uart.c b/app/driver/uart.c new file mode 100644 index 00000000..76a70933 --- /dev/null +++ b/app/driver/uart.c @@ -0,0 +1,250 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: uart.c + * + * Description: Two UART mode configration and interrupt handler. + * Check your hardware connection while use this mode. + * + * Modification history: + * 2014/3/12, v1.0 create this file. +*******************************************************************************/ +#include "ets_sys.h" +#include "osapi.h" +#include "driver/uart.h" +#include "user_config.h" + +#define UART0 0 +#define UART1 1 + +// UartDev is defined and initialized in rom code. +extern UartDevice UartDev; + +LOCAL void uart0_rx_intr_handler(void *para); + +/****************************************************************************** + * FunctionName : uart_config + * Description : Internal used function + * UART0 used for data TX/RX, RX buffer size is 0x100, interrupt enabled + * UART1 just used for debug output + * Parameters : uart_no, use UART0 or UART1 defined ahead + * Returns : NONE +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +uart_config(uint8 uart_no) +{ + if (uart_no == UART1) { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); + } else { + /* rcv_buff size if 0x100 */ + ETS_UART_INTR_ATTACH(uart0_rx_intr_handler, &(UartDev.rcv_buff)); + PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD); + PIN_PULLUP_EN(PERIPHS_IO_MUX_U0RXD_U); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD); + } + + uart_div_modify(uart_no, UART_CLK_FREQ / (UartDev.baut_rate)); + + WRITE_PERI_REG(UART_CONF0(uart_no), UartDev.exist_parity + | UartDev.parity + | (UartDev.stop_bits << UART_STOP_BIT_NUM_S) + | (UartDev.data_bits << UART_BIT_NUM_S)); + + + //clear rx and tx fifo,not ready + SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); + CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); + + //set rx fifo trigger + WRITE_PERI_REG(UART_CONF1(uart_no), (UartDev.rcv_buff.TrigLvl & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S); + + //clear all interrupt + WRITE_PERI_REG(UART_INT_CLR(uart_no), 0xffff); + //enable rx_interrupt + SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA); +} + +/****************************************************************************** + * FunctionName : uart_tx_one_char + * Description : Internal used function + * Use uart interface to transfer one char + * Parameters : uint8 TxChar - character to tx + * Returns : OK +*******************************************************************************/ +STATUS ICACHE_FLASH_ATTR +uart_tx_one_char(uint8 uart, uint8 TxChar) +{ + while (true) + { + uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT<> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) { + break; + } + } + + WRITE_PERI_REG(UART_FIFO(uart) , TxChar); + return OK; +} + +/****************************************************************************** + * FunctionName : uart1_write_char + * Description : Internal used function + * Do some special deal while tx char is '\r' or '\n' + * Parameters : char c - character to tx + * Returns : NONE +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +uart1_write_char(char c) +{ + if (c == '\n') + { + uart_tx_one_char(UART1, '\r'); + uart_tx_one_char(UART1, '\n'); + } + else if (c == '\r') + { + } + else + { + uart_tx_one_char(UART1, c); + } +} + +/****************************************************************************** + * FunctionName : uart0_tx_buffer + * Description : use uart0 to transfer buffer + * Parameters : uint8 *buf - point to send buffer + * uint16 len - buffer len + * Returns : +*******************************************************************************/ +void ICACHE_FLASH_ATTR +uart0_tx_buffer(uint8 *buf, uint16 len) +{ + uint16 i; + + for (i = 0; i < len; i++) + { + uart_tx_one_char(UART0, buf[i]); + } +} + +/****************************************************************************** + * FunctionName : uart0_sendStr + * Description : use uart0 to transfer buffer + * Parameters : uint8 *buf - point to send buffer + * uint16 len - buffer len + * Returns : +*******************************************************************************/ +void ICACHE_FLASH_ATTR uart0_sendStr(const char *str) +{ + while(*str) + { + // uart_tx_one_char(UART0, *str++); + uart0_putc(*str++); + } +} + +/****************************************************************************** + * FunctionName : uart0_putc + * Description : use uart0 to transfer char + * Parameters : uint8 c - send char + * Returns : +*******************************************************************************/ +void ICACHE_FLASH_ATTR uart0_putc(const char c) +{ + if (c == '\n') + { + uart_tx_one_char(UART0, '\r'); + uart_tx_one_char(UART0, '\n'); + } + else if (c == '\r') + { + } + else + { + uart_tx_one_char(UART0, c); + } +} + +/****************************************************************************** + * FunctionName : uart0_rx_intr_handler + * Description : Internal used function + * UART0 interrupt handler, add self handle code inside + * Parameters : void *para - point to ETS_UART_INTR_ATTACH's arg + * Returns : NONE +*******************************************************************************/ +LOCAL void +uart0_rx_intr_handler(void *para) +{ + /* uart0 and uart1 intr combine togther, when interrupt occur, see reg 0x3ff20020, bit2, bit0 represents + * uart1 and uart0 respectively + */ + RcvMsgBuff *pRxBuff = (RcvMsgBuff *)para; + uint8 RcvChar; + + if (UART_RXFIFO_FULL_INT_ST != (READ_PERI_REG(UART_INT_ST(UART0)) & UART_RXFIFO_FULL_INT_ST)) { + return; + } + + WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR); + + while (READ_PERI_REG(UART_STATUS(UART0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) { + RcvChar = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF; + + /* you can add your handle code below.*/ + + *(pRxBuff->pWritePos) = RcvChar; + + // insert here for get one command line from uart + if (RcvChar == '\r' || RcvChar == '\n' ) { + pRxBuff->BuffState = WRITE_OVER; + } + + if (pRxBuff->pWritePos == (pRxBuff->pRcvMsgBuff + RX_BUFF_SIZE)) { + // overflow ...we may need more error handle here. + pRxBuff->pWritePos = pRxBuff->pRcvMsgBuff ; + } else { + pRxBuff->pWritePos++; + } + + if (pRxBuff->pWritePos == pRxBuff->pReadPos){ // overflow one byte, need push pReadPos one byte ahead + if (pRxBuff->pReadPos == (pRxBuff->pRcvMsgBuff + RX_BUFF_SIZE)) { + pRxBuff->pReadPos = pRxBuff->pRcvMsgBuff ; + } else { + pRxBuff->pReadPos++; + } + } + } +} + +/****************************************************************************** + * FunctionName : uart_init + * Description : user interface for init uart + * Parameters : UartBautRate uart0_br - uart0 bautrate + * UartBautRate uart1_br - uart1 bautrate + * Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +uart_init(UartBautRate uart0_br, UartBautRate uart1_br) +{ + // rom use 74880 baut_rate, here reinitialize + UartDev.baut_rate = uart0_br; + uart_config(UART0); + UartDev.baut_rate = uart1_br; + uart_config(UART1); + ETS_UART_INTR_ENABLE(); + + // install uart1 putc callback +#ifndef NODE_DEBUG + os_install_putc1((void *)uart1_write_char); +#endif +} + +void ICACHE_FLASH_ATTR +uart_setup(uint8 uart_no) +{ + ETS_UART_INTR_DISABLE(); + uart_config(uart_no); + ETS_UART_INTR_ENABLE(); +} diff --git a/app/gen_misc.bat b/app/gen_misc.bat new file mode 100644 index 00000000..fb9c5d0e --- /dev/null +++ b/app/gen_misc.bat @@ -0,0 +1,27 @@ +@echo off +set BACKPATH=%PATH% +set PATH=%BACKPATH%;%CD%\..\tools +@echo on + +del /F ..\bin\eagle.app.v6.flash.bin ..\bin\eagle.app.v6.irom0text.bin ..\bin\eagle.app.v6.dump ..\bin\eagle.app.v6.S + +cd .output\eagle\debug\image + +xt-objdump -x -s eagle.app.v6.out > ..\..\..\..\..\bin\eagle.app.v6.dump +xt-objdump -S eagle.app.v6.out > ..\..\..\..\..\bin\eagle.app.v6.S + +xt-objcopy --only-section .text -O binary eagle.app.v6.out eagle.app.v6.text.bin +xt-objcopy --only-section .data -O binary eagle.app.v6.out eagle.app.v6.data.bin +xt-objcopy --only-section .rodata -O binary eagle.app.v6.out eagle.app.v6.rodata.bin +xt-objcopy --only-section .irom0.text -O binary eagle.app.v6.out eagle.app.v6.irom0text.bin + +gen_appbin.py eagle.app.v6.out v6 + +xcopy /y eagle.app.v6.irom0text.bin ..\..\..\..\..\bin\ +xcopy /y eagle.app.v6.flash.bin ..\..\..\..\..\bin\ + +cd ..\..\..\..\ + +@echo off +set PATH=%BACKPATH% +@echo on diff --git a/app/gen_misc.sh b/app/gen_misc.sh new file mode 100644 index 00000000..eeada235 --- /dev/null +++ b/app/gen_misc.sh @@ -0,0 +1,25 @@ +#!/bin/bash -x +make +if [ $? == 0 ];then +rm ../bin/eagle.app.v6.flash.bin ../bin/eagle.app.v6.irom0text.bin ../bin/eagle.app.v6.dump ../bin/eagle.app.v6.S + +cd .output/eagle/debug/image + +xt-objdump -x -s eagle.app.v6.out > ../../../../../bin/eagle.app.v6.dump +xt-objdump -S eagle.app.v6.out > ../../../../../bin/eagle.app.v6.S + +xt-objcopy --only-section .text -O binary eagle.app.v6.out eagle.app.v6.text.bin +xt-objcopy --only-section .data -O binary eagle.app.v6.out eagle.app.v6.data.bin +xt-objcopy --only-section .rodata -O binary eagle.app.v6.out eagle.app.v6.rodata.bin +xt-objcopy --only-section .irom0.text -O binary eagle.app.v6.out eagle.app.v6.irom0text.bin + +../../../../../tools/gen_appbin.py eagle.app.v6.out v6 + +cp eagle.app.v6.irom0text.bin ../../../../../bin/ +cp eagle.app.v6.flash.bin ../../../../../bin/ + +cd ../../../../../ + +else +echo "make error" +fi diff --git a/app/gen_misc_plus.bat b/app/gen_misc_plus.bat new file mode 100644 index 00000000..3aaea129 --- /dev/null +++ b/app/gen_misc_plus.bat @@ -0,0 +1,27 @@ +@echo off +set BACKPATH=%PATH% +set PATH=%BACKPATH%;%CD%\..\tools +@echo on + +rm ..\bin\upgrade\%1.bin + +cd .output\eagle\debug\image\ + +xt-objcopy --only-section .text -O binary eagle.app.v6.out eagle.app.v6.text.bin +xt-objcopy --only-section .data -O binary eagle.app.v6.out eagle.app.v6.data.bin +xt-objcopy --only-section .rodata -O binary eagle.app.v6.out eagle.app.v6.rodata.bin +xt-objcopy --only-section .irom0.text -O binary eagle.app.v6.out eagle.app.v6.irom0text.bin + +gen_appbin.py eagle.app.v6.out v6 + +gen_flashbin.py eagle.app.v6.flash.bin eagle.app.v6.irom0text.bin + +cp eagle.app.flash.bin %1.bin + +xcopy /y %1.bin ..\..\..\..\..\bin\upgrade\ + +cd ..\..\..\..\ + +@echo off +set PATH=%BACKPATH% +@echo on \ No newline at end of file diff --git a/app/gen_misc_plus.sh b/app/gen_misc_plus.sh new file mode 100644 index 00000000..7c4cb42e --- /dev/null +++ b/app/gen_misc_plus.sh @@ -0,0 +1,28 @@ +#!/bin/bash -x +touch user/user_main.c +make APP=$1 +if [ $? == 0 ];then +rm ../bin/upgrade/user$1.bin ../bin/upgrade/user$1.dump ../bin/upgrade/user$1.S + +cd .output/eagle/debug/image/ + +xt-objdump -x -s eagle.app.v6.out > ../../../../../bin/upgrade/user$1.dump +xt-objdump -S eagle.app.v6.out > ../../../../../bin/upgrade/user$1.S + +xt-objcopy --only-section .text -O binary eagle.app.v6.out eagle.app.v6.text.bin +xt-objcopy --only-section .data -O binary eagle.app.v6.out eagle.app.v6.data.bin +xt-objcopy --only-section .rodata -O binary eagle.app.v6.out eagle.app.v6.rodata.bin +xt-objcopy --only-section .irom0.text -O binary eagle.app.v6.out eagle.app.v6.irom0text.bin + +../../../../../tools/gen_appbin.py eagle.app.v6.out v6 + +../../../../../tools/gen_flashbin.py eagle.app.v6.flash.bin eagle.app.v6.irom0text.bin + +cp eagle.app.flash.bin user$1.bin +cp user$1.bin ../../../../../bin/upgrade/ + +cd ../../../../../ + +else +echo "make error" +fi diff --git a/app/include/arch/cc.h b/app/include/arch/cc.h new file mode 100644 index 00000000..ff03b307 --- /dev/null +++ b/app/include/arch/cc.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __ARCH_CC_H__ +#define __ARCH_CC_H__ + +//#include +#include "c_types.h" +#include "ets_sys.h" +#include "osapi.h" +#define EFAULT 14 + +//#define LWIP_PROVIDE_ERRNO + +#if (1) +#define BYTE_ORDER LITTLE_ENDIAN +#else +#define BYTE_ORDER BIG_ENDIAN +#endif + + +typedef unsigned char u8_t; +typedef signed char s8_t; +typedef unsigned short u16_t; +typedef signed short s16_t; +typedef unsigned long u32_t; +typedef signed long s32_t; +typedef unsigned long mem_ptr_t; + +#define S16_F "d" +#define U16_F "d" +#define X16_F "x" + +#define S32_F "d" +#define U32_F "d" +#define X32_F "x" + + + +//#define PACK_STRUCT_FIELD(x) x __attribute__((packed)) +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_STRUCT __attribute__((packed)) +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_END + +//#define LWIP_DEBUG + +#ifdef LWIP_DEBUG +#define LWIP_PLATFORM_DIAG(x) os_printf x +#define LWIP_PLATFORM_ASSERT(x) ETS_ASSERT(x) +#else +#define LWIP_PLATFORM_DIAG(x) +#define LWIP_PLATFORM_ASSERT(x) +#endif + +#define SYS_ARCH_DECL_PROTECT(x) +#define SYS_ARCH_PROTECT(x) +#define SYS_ARCH_UNPROTECT(x) + +#define LWIP_PLATFORM_BYTESWAP 1 +#define LWIP_PLATFORM_HTONS(_n) ((u16_t)((((_n) & 0xff) << 8) | (((_n) >> 8) & 0xff))) +#define LWIP_PLATFORM_HTONL(_n) ((u32_t)( (((_n) & 0xff) << 24) | (((_n) & 0xff00) << 8) | (((_n) >> 8) & 0xff00) | (((_n) >> 24) & 0xff) )) + +#if LWIP_RAW +extern u8_t memp_memory_RAW_PCB_base[]; +#endif /* LWIP_RAW */ + +#if LWIP_UDP +extern u8_t memp_memory_UDP_PCB_base[]; +#endif /* LWIP_UDP */ + +#if LWIP_TCP +extern u8_t memp_memory_TCP_PCB_base[]; +extern u8_t memp_memory_TCP_PCB_LISTEN_base[]; +extern u8_t memp_memory_TCP_SEG_base[] SHMEM_ATTR; +#endif /* LWIP_TCP */ + +#if (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) /* LWIP_TIMERS */ +extern u8_t memp_memory_SYS_TIMEOUT_base[]; +#endif /* LWIP_TIMERS */ + +extern u8_t memp_memory_PBUF_base[]; +extern u8_t memp_memory_PBUF_POOL_base[]; + + + +#endif /* __ARCH_CC_H__ */ diff --git a/app/include/arch/perf.h b/app/include/arch/perf.h new file mode 100644 index 00000000..089facac --- /dev/null +++ b/app/include/arch/perf.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __PERF_H__ +#define __PERF_H__ + +#define PERF_START /* null definition */ +#define PERF_STOP(x) /* null definition */ + +#endif /* __PERF_H__ */ diff --git a/app/include/arch/sys_arch.h b/app/include/arch/sys_arch.h new file mode 100644 index 00000000..e69de29b diff --git a/app/include/driver/gpio16.h b/app/include/driver/gpio16.h new file mode 100644 index 00000000..998bda1f --- /dev/null +++ b/app/include/driver/gpio16.h @@ -0,0 +1,9 @@ +#ifndef __GPIO16_H__ +#define __GPIO16_H__ + +void gpio16_output_conf(void); +void gpio16_output_set(uint8 value); +void gpio16_input_conf(void); +uint8 gpio16_input_get(void); + +#endif diff --git a/app/include/driver/i2c_master.h b/app/include/driver/i2c_master.h new file mode 100644 index 00000000..22f7443b --- /dev/null +++ b/app/include/driver/i2c_master.h @@ -0,0 +1,69 @@ +#ifndef __I2C_MASTER_H__ +#define __I2C_MASTER_H__ + +#define I2C_MASTER_SDA_MUX (pin_mux[sda]) +#define I2C_MASTER_SCL_MUX (pin_mux[scl]) +#define I2C_MASTER_SDA_GPIO (pinSDA) +#define I2C_MASTER_SCL_GPIO (pinSCL) +#define I2C_MASTER_SDA_FUNC (pin_func[sda]) +#define I2C_MASTER_SCL_FUNC (pin_func[scl]) + +// #define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO2_U +// #define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_MTDO_U +// #define I2C_MASTER_SDA_GPIO 2 +// #define I2C_MASTER_SCL_GPIO 15 +// #define I2C_MASTER_SDA_FUNC FUNC_GPIO2 +// #define I2C_MASTER_SCL_FUNC FUNC_GPIO15 + +// #define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO2_U +// #define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_MTMS_U +// #define I2C_MASTER_SDA_GPIO 2 +// #define I2C_MASTER_SCL_GPIO 14 +// #define I2C_MASTER_SDA_FUNC FUNC_GPIO2 +// #define I2C_MASTER_SCL_FUNC FUNC_GPIO14 + +//#define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO2_U +//#define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_GPIO0_U +//#define I2C_MASTER_SDA_GPIO 2 +//#define I2C_MASTER_SCL_GPIO 0 +//#define I2C_MASTER_SDA_FUNC FUNC_GPIO2 +//#define I2C_MASTER_SCL_FUNC FUNC_GPIO0 + +#if 0 +#define I2C_MASTER_GPIO_SET(pin) \ + gpio_output_set(1< + * + */ +#ifndef __LWIP_API_H__ +#define __LWIP_API_H__ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + +#include "lwip/netbuf.h" +#include "lwip/sys.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ + +/* Flags for netconn_write (u8_t) */ +#define NETCONN_NOFLAG 0x00 +#define NETCONN_NOCOPY 0x00 /* Only for source code compatibility */ +#define NETCONN_COPY 0x01 +#define NETCONN_MORE 0x02 +#define NETCONN_DONTBLOCK 0x04 + +/* Flags for struct netconn.flags (u8_t) */ +/** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores whether to wake up the original application task + if data couldn't be sent in the first try. */ +#define NETCONN_FLAG_WRITE_DELAYED 0x01 +/** Should this netconn avoid blocking? */ +#define NETCONN_FLAG_NON_BLOCKING 0x02 +/** Was the last connect action a non-blocking one? */ +#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT 0x04 +/** If this is set, a TCP netconn must call netconn_recved() to update + the TCP receive window (done automatically if not set). */ +#define NETCONN_FLAG_NO_AUTO_RECVED 0x08 +/** If a nonblocking write has been rejected before, poll_tcp needs to + check if the netconn is writable again */ +#define NETCONN_FLAG_CHECK_WRITESPACE 0x10 + + +/* Helpers to process several netconn_types by the same code */ +#define NETCONNTYPE_GROUP(t) (t&0xF0) +#define NETCONNTYPE_DATAGRAM(t) (t&0xE0) + +/** Protocol family and type of the netconn */ +enum netconn_type { + NETCONN_INVALID = 0, + /* NETCONN_TCP Group */ + NETCONN_TCP = 0x10, + /* NETCONN_UDP Group */ + NETCONN_UDP = 0x20, + NETCONN_UDPLITE = 0x21, + NETCONN_UDPNOCHKSUM= 0x22, + /* NETCONN_RAW Group */ + NETCONN_RAW = 0x40 +}; + +/** Current state of the netconn. Non-TCP netconns are always + * in state NETCONN_NONE! */ +enum netconn_state { + NETCONN_NONE, + NETCONN_WRITE, + NETCONN_LISTEN, + NETCONN_CONNECT, + NETCONN_CLOSE +}; + +/** Use to inform the callback function about changes */ +enum netconn_evt { + NETCONN_EVT_RCVPLUS, + NETCONN_EVT_RCVMINUS, + NETCONN_EVT_SENDPLUS, + NETCONN_EVT_SENDMINUS, + NETCONN_EVT_ERROR +}; + +#if LWIP_IGMP +/** Used for netconn_join_leave_group() */ +enum netconn_igmp { + NETCONN_JOIN, + NETCONN_LEAVE +}; +#endif /* LWIP_IGMP */ + +/* forward-declare some structs to avoid to include their headers */ +struct ip_pcb; +struct tcp_pcb; +struct udp_pcb; +struct raw_pcb; +struct netconn; +struct api_msg_msg; + +/** A callback prototype to inform about events for a netconn */ +typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len); + +/** A netconn descriptor */ +struct netconn { + /** type of the netconn (TCP, UDP or RAW) */ + enum netconn_type type; + /** current state of the netconn */ + enum netconn_state state; + /** the lwIP internal protocol control block */ + union { + struct ip_pcb *ip; + struct tcp_pcb *tcp; + struct udp_pcb *udp; + struct raw_pcb *raw; + } pcb; + /** the last error this netconn had */ + err_t last_err; + /** sem that is used to synchroneously execute functions in the core context */ + sys_sem_t op_completed; + /** mbox where received packets are stored until they are fetched + by the netconn application thread (can grow quite big) */ + sys_mbox_t recvmbox; +#if LWIP_TCP + /** mbox where new connections are stored until processed + by the application thread */ + sys_mbox_t acceptmbox; +#endif /* LWIP_TCP */ + /** only used for socket layer */ +#if LWIP_SOCKET + int socket; +#endif /* LWIP_SOCKET */ +#if LWIP_SO_RCVTIMEO + /** timeout to wait for new data to be received + (or connections to arrive for listening netconns) */ + int recv_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + /** maximum amount of bytes queued in recvmbox + not used for TCP: adjust TCP_WND instead! */ + int recv_bufsize; + /** number of bytes currently in recvmbox to be received, + tested against recv_bufsize to limit bytes on recvmbox + for UDP and RAW, used for FIONREAD */ + s16_t recv_avail; +#endif /* LWIP_SO_RCVBUF */ + /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */ + u8_t flags; +#if LWIP_TCP + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores how much is already sent. */ + size_t write_offset; + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores the message. + Also used during connect and close. */ + struct api_msg_msg *current_msg; +#endif /* LWIP_TCP */ + /** A callback function that is informed about events for this netconn */ + netconn_callback callback; +}; + +/** Register an Network connection event */ +#define API_EVENT(c,e,l) if (c->callback) { \ + (*c->callback)(c, e, l); \ + } + +/** Set conn->last_err to err but don't overwrite fatal errors */ +#define NETCONN_SET_SAFE_ERR(conn, err) do { \ + SYS_ARCH_DECL_PROTECT(lev); \ + SYS_ARCH_PROTECT(lev); \ + if (!ERR_IS_FATAL((conn)->last_err)) { \ + (conn)->last_err = err; \ + } \ + SYS_ARCH_UNPROTECT(lev); \ +} while(0); + +/* Network connection functions: */ +#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL) +#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c) +struct +netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, + netconn_callback callback); +err_t netconn_delete(struct netconn *conn); +/** Get the type of a netconn (as enum netconn_type). */ +#define netconn_type(conn) (conn->type) + +err_t netconn_getaddr(struct netconn *conn, ip_addr_t *addr, + u16_t *port, u8_t local); +#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0) +#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1) + +err_t netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port); +err_t netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port); +err_t netconn_disconnect (struct netconn *conn); +err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog); +#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG) +err_t netconn_accept(struct netconn *conn, struct netconn **new_conn); +err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf); +err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf); +void netconn_recved(struct netconn *conn, u32_t length); +err_t netconn_sendto(struct netconn *conn, struct netbuf *buf, + ip_addr_t *addr, u16_t port); +err_t netconn_send(struct netconn *conn, struct netbuf *buf); +err_t netconn_write(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags); +err_t netconn_close(struct netconn *conn); +err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx); + +#if LWIP_IGMP +err_t netconn_join_leave_group(struct netconn *conn, ip_addr_t *multiaddr, + ip_addr_t *netif_addr, enum netconn_igmp join_or_leave); +#endif /* LWIP_IGMP */ +#if LWIP_DNS +err_t netconn_gethostbyname(const char *name, ip_addr_t *addr); +#endif /* LWIP_DNS */ + +#define netconn_err(conn) ((conn)->last_err) +#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize) + +/** Set the blocking status of netconn calls (@todo: write/send is missing) */ +#define netconn_set_nonblocking(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_NON_BLOCKING; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_NON_BLOCKING; }} while(0) +/** Get the blocking status of netconn calls (@todo: write/send is missing) */ +#define netconn_is_nonblocking(conn) (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0) + +/** TCP: Set the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */ +#define netconn_set_noautorecved(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_NO_AUTO_RECVED; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_NO_AUTO_RECVED; }} while(0) +/** TCP: Get the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */ +#define netconn_get_noautorecved(conn) (((conn)->flags & NETCONN_FLAG_NO_AUTO_RECVED) != 0) + +#if LWIP_SO_RCVTIMEO +/** Set the receive timeout in milliseconds */ +#define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout)) +/** Get the receive timeout in milliseconds */ +#define netconn_get_recvtimeout(conn) ((conn)->recv_timeout) +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF +/** Set the receive buffer in bytes */ +#define netconn_set_recvbufsize(conn, recvbufsize) ((conn)->recv_bufsize = (recvbufsize)) +/** Get the receive buffer in bytes */ +#define netconn_get_recvbufsize(conn) ((conn)->recv_bufsize) +#endif /* LWIP_SO_RCVBUF*/ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ + +#endif /* __LWIP_API_H__ */ diff --git a/app/include/lwip/api_msg.h b/app/include/lwip/api_msg.h new file mode 100644 index 00000000..f99d8c3b --- /dev/null +++ b/app/include/lwip/api_msg.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_API_MSG_H__ +#define __LWIP_API_MSG_H__ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* For the netconn API, these values are use as a bitmask! */ +#define NETCONN_SHUT_RD 1 +#define NETCONN_SHUT_WR 2 +#define NETCONN_SHUT_RDWR (NETCONN_SHUT_RD | NETCONN_SHUT_WR) + +/* IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ +/** This struct includes everything that is necessary to execute a function + for a netconn in another thread context (mainly used to process netconns + in the tcpip_thread context to be thread safe). */ +struct api_msg_msg { + /** The netconn which to process - always needed: it includes the semaphore + which is used to block the application thread until the function finished. */ + struct netconn *conn; + /** The return value of the function executed in tcpip_thread. */ + err_t err; + /** Depending on the executed function, one of these union members is used */ + union { + /** used for do_send */ + struct netbuf *b; + /** used for do_newconn */ + struct { + u8_t proto; + } n; + /** used for do_bind and do_connect */ + struct { + ip_addr_t *ipaddr; + u16_t port; + } bc; + /** used for do_getaddr */ + struct { + ip_addr_t *ipaddr; + u16_t *port; + u8_t local; + } ad; + /** used for do_write */ + struct { + const void *dataptr; + size_t len; + u8_t apiflags; + } w; + /** used for do_recv */ + struct { + u32_t len; + } r; + /** used for do_close (/shutdown) */ + struct { + u8_t shut; + } sd; +#if LWIP_IGMP + /** used for do_join_leave_group */ + struct { + ip_addr_t *multiaddr; + ip_addr_t *netif_addr; + enum netconn_igmp join_or_leave; + } jl; +#endif /* LWIP_IGMP */ +#if TCP_LISTEN_BACKLOG + struct { + u8_t backlog; + } lb; +#endif /* TCP_LISTEN_BACKLOG */ + } msg; +}; + +/** This struct contains a function to execute in another thread context and + a struct api_msg_msg that serves as an argument for this function. + This is passed to tcpip_apimsg to execute functions in tcpip_thread context. */ +struct api_msg { + /** function to execute in tcpip_thread context */ + void (* function)(struct api_msg_msg *msg); + /** arguments for this function */ + struct api_msg_msg msg; +}; + +#if LWIP_DNS +/** As do_gethostbyname requires more arguments but doesn't require a netconn, + it has its own struct (to avoid struct api_msg getting bigger than necessary). + do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg + (see netconn_gethostbyname). */ +struct dns_api_msg { + /** Hostname to query or dotted IP address string */ + const char *name; + /** Rhe resolved address is stored here */ + ip_addr_t *addr; + /** This semaphore is posted when the name is resolved, the application thread + should wait on it. */ + sys_sem_t *sem; + /** Errors are given back here */ + err_t *err; +}; +#endif /* LWIP_DNS */ + +void do_newconn ( struct api_msg_msg *msg); +void do_delconn ( struct api_msg_msg *msg); +void do_bind ( struct api_msg_msg *msg); +void do_connect ( struct api_msg_msg *msg); +void do_disconnect ( struct api_msg_msg *msg); +void do_listen ( struct api_msg_msg *msg); +void do_send ( struct api_msg_msg *msg); +void do_recv ( struct api_msg_msg *msg); +void do_write ( struct api_msg_msg *msg); +void do_getaddr ( struct api_msg_msg *msg); +void do_close ( struct api_msg_msg *msg); +void do_shutdown ( struct api_msg_msg *msg); +#if LWIP_IGMP +void do_join_leave_group( struct api_msg_msg *msg); +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +void do_gethostbyname(void *arg); +#endif /* LWIP_DNS */ + +struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback); +void netconn_free(struct netconn *conn); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ + +#endif /* __LWIP_API_MSG_H__ */ diff --git a/app/include/lwip/app/dhcpserver.h b/app/include/lwip/app/dhcpserver.h new file mode 100644 index 00000000..b6f7a0b3 --- /dev/null +++ b/app/include/lwip/app/dhcpserver.h @@ -0,0 +1,89 @@ +#ifndef __DHCPS_H__ +#define __DHCPS_H__ + +typedef struct dhcps_state{ + sint16_t state; +} dhcps_state; + +// ����dhcpclient�Զ����һ��DHCP msg�ṹ�� +typedef struct dhcps_msg { + uint8_t op, htype, hlen, hops; + uint8_t xid[4]; + uint16_t secs, flags; + uint8_t ciaddr[4]; + uint8_t yiaddr[4]; + uint8_t siaddr[4]; + uint8_t giaddr[4]; + uint8_t chaddr[16]; + uint8_t sname[64]; + uint8_t file[128]; + uint8_t options[312]; +}dhcps_msg; + +#ifndef LWIP_OPEN_SRC +struct dhcps_lease { + uint32 start_ip; + uint32 end_ip; +}; +#endif + +struct dhcps_pool{ + struct ip_addr ip; + uint8 mac[6]; + uint32 lease_timer; +}; + +typedef struct _list_node{ + void *pnode; + struct _list_node *pnext; +}list_node; + +#define DHCPS_LEASE_TIMER 0x05A0 +#define DHCPS_MAX_LEASE 0x64 +#define BOOTP_BROADCAST 0x8000 + +#define DHCP_REQUEST 1 +#define DHCP_REPLY 2 +#define DHCP_HTYPE_ETHERNET 1 +#define DHCP_HLEN_ETHERNET 6 +#define DHCP_MSG_LEN 236 + +#define DHCPS_SERVER_PORT 67 +#define DHCPS_CLIENT_PORT 68 + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 + +#define DHCP_OPTION_SUBNET_MASK 1 +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DNS_SERVER 6 +#define DHCP_OPTION_REQ_IPADDR 50 +#define DHCP_OPTION_LEASE_TIME 51 +#define DHCP_OPTION_MSG_TYPE 53 +#define DHCP_OPTION_SERVER_ID 54 +#define DHCP_OPTION_INTERFACE_MTU 26 +#define DHCP_OPTION_PERFORM_ROUTER_DISCOVERY 31 +#define DHCP_OPTION_BROADCAST_ADDRESS 28 +#define DHCP_OPTION_REQ_LIST 55 +#define DHCP_OPTION_END 255 + +//#define USE_CLASS_B_NET 1 +#define DHCPS_DEBUG 0 + + +#define DHCPS_STATE_OFFER 1 +#define DHCPS_STATE_DECLINE 2 +#define DHCPS_STATE_ACK 3 +#define DHCPS_STATE_NAK 4 +#define DHCPS_STATE_IDLE 5 + +void dhcps_start(struct ip_info *info); +void dhcps_stop(void); + +#endif + diff --git a/app/include/lwip/app/espconn.h b/app/include/lwip/app/espconn.h new file mode 100644 index 00000000..e6e60fd1 --- /dev/null +++ b/app/include/lwip/app/espconn.h @@ -0,0 +1,398 @@ +#ifndef __ESPCONN_H__ +#define __ESPCONN_H__ + +#include "lwip/dns.h" +#include "os_type.h" + +#if 0 +#define espconn_printf(fmt, args...) os_printf(fmt,## args) +#else +#define espconn_printf(fmt, args...) +#endif + + +typedef void *espconn_handle; +typedef void (* espconn_connect_callback)(void *arg); +typedef void (* espconn_reconnect_callback)(void *arg, sint8 err); + +/* Definitions for error constants. */ + +#define ESPCONN_OK 0 /* No error, everything OK. */ +#define ESPCONN_MEM -1 /* Out of memory error. */ +#define ESPCONN_TIMEOUT -3 /* Timeout. */ +#define ESPCONN_RTE -4 /* Routing problem. */ +#define ESPCONN_INPROGRESS -5 /* Operation in progress */ + +#define ESPCONN_ABRT -8 /* Connection aborted. */ +#define ESPCONN_RST -9 /* Connection reset. */ +#define ESPCONN_CLSD -10 /* Connection closed. */ +#define ESPCONN_CONN -11 /* Not connected. */ + +#define ESPCONN_ARG -12 /* Illegal argument. */ +#define ESPCONN_ISCONN -15 /* Already connected. */ + +#define ESPCONN_SSL 0x01 +#define ESPCONN_NORM 0x00 + +#define ESPCONN_STA 0x01 +#define ESPCONN_AP 0x02 +#define ESPCONN_AP_STA 0x03 + +#define STA_NETIF 0x00 +#define AP_NETIF 0x01 + +/** Protocol family and type of the espconn */ +enum espconn_type { + ESPCONN_INVALID = 0, + /* ESPCONN_TCP Group */ + ESPCONN_TCP = 0x10, + /* ESPCONN_UDP Group */ + ESPCONN_UDP = 0x20, +}; + +/** Current state of the espconn. Non-TCP espconn are always in state ESPCONN_NONE! */ +enum espconn_state { + ESPCONN_NONE, + ESPCONN_WAIT, + ESPCONN_LISTEN, + ESPCONN_CONNECT, + ESPCONN_WRITE, + ESPCONN_READ, + ESPCONN_CLOSE +}; + +typedef struct _esp_tcp { + int remote_port; + int local_port; + uint8 local_ip[4]; + uint8 remote_ip[4]; + espconn_connect_callback connect_callback; + espconn_reconnect_callback reconnect_callback; + espconn_connect_callback disconnect_callback; +} esp_tcp; + +typedef struct _esp_udp { + int remote_port; + int local_port; + uint8 local_ip[4]; + uint8 remote_ip[4]; +} esp_udp; + +typedef struct _remot_info{ + enum espconn_state state; + int remote_port; + uint8 remote_ip[4]; +}remot_info; + +/** A callback prototype to inform about events for a espconn */ +typedef void (* espconn_recv_callback)(void *arg, char *pdata, unsigned short len); +typedef void (* espconn_sent_callback)(void *arg); + +/** A espconn descriptor */ +struct espconn { + /** type of the espconn (TCP, UDP) */ + enum espconn_type type; + /** current state of the espconn */ + enum espconn_state state; + union { + esp_tcp *tcp; + esp_udp *udp; + } proto; + /** A callback function that is informed about events for this espconn */ + espconn_recv_callback recv_callback; + espconn_sent_callback sent_callback; + uint8 link_cnt; + void *reverse; +}; + +enum espconn_option{ + ESPCONN_REUSEADDR = 1, + ESPCONN_END +}; + +typedef struct _comon_pkt{ + void *pcb; + int remote_port; + uint8 remote_ip[4]; + uint8 *ptrbuf; + uint16 cntr; + uint16 write_len; + uint16 write_total; + sint8 err; + uint32 timeout; + uint32 recv_check; + enum espconn_option espconn_opt; + os_timer_t ptimer; +}comon_pkt; + +typedef struct _espconn_msg{ + struct espconn *pespconn; + comon_pkt pcommon; + uint8 count_opt; + void *preverse; + void *pssl; + struct _espconn_msg *pnext; +}espconn_msg; + +/****************************************************************************** + * FunctionName : espconn_copy_partial + * Description : reconnect with host + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ + +void espconn_copy_partial(struct espconn *pesp_dest, struct espconn *pesp_source); + +/****************************************************************************** + * FunctionName : espconn_copy_partial + * Description : insert the node to the active connection list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ + +void espconn_list_creat(espconn_msg **phead, espconn_msg* pinsert); + +/****************************************************************************** + * FunctionName : espconn_list_delete + * Description : remove the node from the active connection list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ + +void espconn_list_delete(espconn_msg **phead, espconn_msg* pdelete); + +/****************************************************************************** + * FunctionName : espconn_find_connection + * Description : Initialize the server: set up a listening PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build server + * Returns : none + *******************************************************************************/ + +bool espconn_find_connection(struct espconn *pespconn, espconn_msg **pnode); + +/****************************************************************************** + * FunctionName : espconn_get_connection_info + * Description : used to specify the function that should be called when disconnect + * Parameters : espconn -- espconn to set the err callback + * discon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ + +sint8 espconn_get_connection_info(struct espconn *pespconn, remot_info **pcon_info, uint8 typeflags); + +/****************************************************************************** + * FunctionName : espconn_connect + * Description : The function given as the connect + * Parameters : espconn -- the espconn used to listen the connection + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_connect(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_disconnect + * Description : disconnect with host + * Parameters : espconn -- the espconn used to disconnect the connection + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_disconnect(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_delete + * Description : disconnect with host + * Parameters : espconn -- the espconn used to disconnect the connection + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_delete(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_accept + * Description : The function given as the listen + * Parameters : espconn -- the espconn used to listen the connection + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_accept(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_create + * Description : sent data for client or server + * Parameters : espconn -- espconn to the data transmission + * Returns : result +*******************************************************************************/ + +extern sint8 espconn_create(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_con + * Description : get the number of simulatenously active TCP connections + * Parameters : none + * Returns : none +*******************************************************************************/ + +extern uint8 espconn_tcp_get_max_con(void); + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_con + * Description : set the number of simulatenously active TCP connections + * Parameters : num -- total number + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_tcp_set_max_con(uint8 num); +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_con_allow + * Description : get the count of simulatenously active connections on the server + * Parameters : espconn -- espconn to get the count + * Returns : result +*******************************************************************************/ + +extern sint8 espconn_tcp_get_max_con_allow(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_con_allow + * Description : set the count of simulatenously active connections on the server + * Parameters : espconn -- espconn to set the count + * Returns : result +*******************************************************************************/ + +extern sint8 espconn_tcp_set_max_con_allow(struct espconn *espconn, uint8 num); + +/****************************************************************************** + * FunctionName : espconn_regist_time + * Description : used to specify the time that should be called when don't recv data + * Parameters : espconn -- the espconn used to the connection + * interval -- the timer when don't recv data + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_regist_time(struct espconn *espconn, uint32 interval, uint8 type_flag); + +/****************************************************************************** + * FunctionName : espconn_regist_sentcb + * Description : Used to specify the function that should be called when data + * has been successfully delivered to the remote host. + * Parameters : struct espconn *espconn -- espconn to set the sent callback + * espconn_sent_callback sent_cb -- sent callback function to + * call for this espconn when data is successfully sent + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_regist_sentcb(struct espconn *espconn, espconn_sent_callback sent_cb); + +/****************************************************************************** + * FunctionName : espconn_sent + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_sent(struct espconn *espconn, uint8 *psent, uint16 length); + +/****************************************************************************** + * FunctionName : espconn_regist_connectcb + * Description : used to specify the function that should be called when + * connects to host. + * Parameters : espconn -- espconn to set the connect callback + * connect_cb -- connected callback function to call when connected + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_regist_connectcb(struct espconn *espconn, espconn_connect_callback connect_cb); + +/****************************************************************************** + * FunctionName : espconn_regist_recvcb + * Description : used to specify the function that should be called when recv + * data from host. + * Parameters : espconn -- espconn to set the recv callback + * recv_cb -- recv callback function to call when recv data + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_regist_recvcb(struct espconn *espconn, espconn_recv_callback recv_cb); + +/****************************************************************************** + * FunctionName : espconn_regist_reconcb + * Description : used to specify the function that should be called when connection + * because of err disconnect. + * Parameters : espconn -- espconn to set the err callback + * recon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_regist_reconcb(struct espconn *espconn, espconn_reconnect_callback recon_cb); + +/****************************************************************************** + * FunctionName : espconn_regist_disconcb + * Description : used to specify the function that should be called when disconnect + * Parameters : espconn -- espconn to set the err callback + * discon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_regist_disconcb(struct espconn *espconn, espconn_connect_callback discon_cb); + +/****************************************************************************** + * FunctionName : espconn_port + * Description : access port value for client so that we don't end up bouncing + * all connections at the same time . + * Parameters : none + * Returns : access port value +*******************************************************************************/ + +extern uint32 espconn_port(void); + +/****************************************************************************** + * FunctionName : espconn_set_opt + * Description : access port value for client so that we don't end up bouncing + * all connections at the same time . + * Parameters : none + * Returns : access port value +*******************************************************************************/ +extern sint8 espconn_set_opt(struct espconn *espconn, uint8 opt); + +/****************************************************************************** + * FunctionName : espconn_gethostbyname + * Description : Resolve a hostname (string) into an IP address. + * Parameters : pespconn -- espconn to resolve a hostname + * hostname -- the hostname that is to be queried + * addr -- pointer to a ip_addr_t where to store the address if + * it is already cached in the dns_table (only valid if + * ESPCONN_OK is returned!) + * found -- a callback function to be called on success, failure + * or timeout (only if ERR_INPROGRESS is returned!) + * Returns : err_t return code + * - ESPCONN_OK if hostname is a valid IP address string or the host + * name is already in the local names table. + * - ESPCONN_INPROGRESS enqueue a request to be sent to the DNS server + * for resolution if no errors are present. + * - ESPCONN_ARG: dns client not initialized or invalid hostname +*******************************************************************************/ + +extern sint8 espconn_gethostbyname(struct espconn *pespconn, const char *name, ip_addr_t *addr, dns_found_callback found); + +/****************************************************************************** + * FunctionName : espconn_igmp_join + * Description : join a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +extern sint8 espconn_igmp_join(ip_addr_t *host_ip, ip_addr_t *multicast_ip); + +/****************************************************************************** + * FunctionName : espconn_igmp_leave + * Description : leave a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +extern sint8 espconn_igmp_leave(ip_addr_t *host_ip, ip_addr_t *multicast_ip); + +#endif + diff --git a/app/include/lwip/app/espconn_tcp.h b/app/include/lwip/app/espconn_tcp.h new file mode 100644 index 00000000..717c0aea --- /dev/null +++ b/app/include/lwip/app/espconn_tcp.h @@ -0,0 +1,43 @@ +#ifndef __ESPCONN_TCP_H__ +#define __ESPCONN_TCP_H__ + +#ifndef ESPCONN_TCP_DEBUG +#define ESPCONN_TCP_DEBUG LWIP_DBG_OFF +#endif +#include "lwip/app/espconn.h" + +#ifndef ESPCONN_TCP_TIMER +#define ESPCONN_TCP_TIMER 40 +#endif + +/****************************************************************************** + * FunctionName : espconn_tcp_disconnect + * Description : A new incoming connection has been disconnected. + * Parameters : espconn -- the espconn used to disconnect with host + * Returns : none +*******************************************************************************/ + +extern void espconn_tcp_disconnect(espconn_msg *pdiscon); + +/****************************************************************************** + * FunctionName : espconn_tcp_client + * Description : Initialize the client: set up a connect PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build client + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_tcp_client(struct espconn* espconn); + +/****************************************************************************** + * FunctionName : espconn_tcp_server + * Description : Initialize the server: set up a listening PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build server + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_tcp_server(struct espconn *espconn); + +#endif /* __CLIENT_TCP_H__ */ + diff --git a/app/include/lwip/app/espconn_udp.h b/app/include/lwip/app/espconn_udp.h new file mode 100644 index 00000000..ad34a7a1 --- /dev/null +++ b/app/include/lwip/app/espconn_udp.h @@ -0,0 +1,51 @@ +#ifndef __ESPCONN_UDP_H__ +#define __ESPCONN_UDP_H__ + +#ifndef ESPCONN_UDP_DEBUG +#define ESPCONN_UDP_DEBUG LWIP_DBG_OFF +#endif + +#include "lwip/app/espconn.h" + +/****************************************************************************** + * FunctionName : espconn_udp_client + * Description : Initialize the client: set up a PCB and bind it to the port + * Parameters : pespconn -- the espconn used to build client + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_udp_client(struct espconn *pespconn); + +/****************************************************************************** + * FunctionName : espconn_udp_disconnect + * Description : A new incoming connection has been disconnected. + * Parameters : espconn -- the espconn used to disconnect with host + * Returns : none +*******************************************************************************/ + +extern void espconn_udp_disconnect(espconn_msg *pdiscon); + +/****************************************************************************** + * FunctionName : espconn_udp_server + * Description : Initialize the server: set up a PCB and bind it to the port + * Parameters : pespconn -- the espconn used to build server + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_udp_server(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_udp_sent + * Description : sent data for client or server + * Parameters : void *arg -- client or server to send + * uint8* psent -- Data to send + * uint16 length -- Length of data to send + * Returns : none +*******************************************************************************/ + +extern void espconn_udp_sent(void *arg, uint8 *psent, uint16 length); + + +#endif /* __ESPCONN_UDP_H__ */ + + diff --git a/app/include/lwip/app/ping.h b/app/include/lwip/app/ping.h new file mode 100644 index 00000000..21e26e91 --- /dev/null +++ b/app/include/lwip/app/ping.h @@ -0,0 +1,85 @@ +#ifndef __PING_H__ +#define __PING_H__ +#include "lwip/ip_addr.h" +#include "lwip/icmp.h" +/** + * PING_USE_SOCKETS: Set to 1 to use sockets, otherwise the raw api is used + */ +#ifndef PING_USE_SOCKETS +#define PING_USE_SOCKETS LWIP_SOCKET +#endif + +/** + * PING_DEBUG: Enable debugging for PING. + */ +#ifndef PING_DEBUG +#define PING_DEBUG LWIP_DBG_OFF +#endif + +/** ping receive timeout - in milliseconds */ +#ifndef PING_RCV_TIMEO +#define PING_RCV_TIMEO 1000 +#endif + +/** ping delay - in milliseconds */ +#ifndef PING_COARSE +#define PING_COARSE 1000 +#endif + +/** ping identifier - must fit on a u16_t */ +#ifndef PING_ID +#define PING_ID 0xAFAF +#endif + +/** ping additional data size to include in the packet */ +#ifndef PING_DATA_SIZE +#define PING_DATA_SIZE 32 +#endif + +/** ping result action - no default action */ +#ifndef PING_RESULT +#define PING_RESULT(ping_ok) +#endif + +#define DEFAULT_PING_MAX_COUNT 4 +#define PING_TIMEOUT_MS 1000 + +typedef void (* ping_recv_function)(void* arg, void *pdata); +typedef void (* ping_sent_function)(void* arg, void *pdata); + +struct ping_option{ + uint32 count; + uint32 ip; + uint32 coarse_time; + ping_recv_function recv_function; + ping_sent_function sent_function; + void* reverse; +}; + +struct ping_msg{ + struct ping_option *ping_opt; + struct raw_pcb *ping_pcb; + uint32 ping_start; + uint32 ping_sent; + uint32 timeout_count; + uint32 max_count; + uint32 sent_count; + uint32 coarse_time; +}; + +struct ping_resp{ + uint32 total_count; + uint32 resp_time; + uint32 seqno; + uint32 timeout_count; + uint32 bytes; + uint32 total_bytes; + uint32 total_time; + sint8 ping_err; +}; + +bool ping_start(struct ping_option *ping_opt); +bool ping_regist_recv(struct ping_option *ping_opt, ping_recv_function ping_recv); +bool ping_regist_sent(struct ping_option *ping_opt, ping_sent_function ping_sent); + +#endif /* __PING_H__ */ diff --git a/app/include/lwip/arch.h b/app/include/lwip/arch.h new file mode 100644 index 00000000..524af6be --- /dev/null +++ b/app/include/lwip/arch.h @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ARCH_H__ +#define __LWIP_ARCH_H__ + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 +#endif + +#include "arch/cc.h" + +/** Temporary: define format string for size_t if not defined in cc.h */ +#ifndef SZT_F +#define SZT_F U32_F +#endif /* SZT_F */ +/** Temporary upgrade helper: define format string for u8_t as hex if not + defined in cc.h */ +#ifndef X8_F +#define X8_F "02x" +#endif /* X8_F */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef PACK_STRUCT_BEGIN +#define PACK_STRUCT_BEGIN +#endif /* PACK_STRUCT_BEGIN */ + +#ifndef PACK_STRUCT_END +#define PACK_STRUCT_END +#endif /* PACK_STRUCT_END */ + +#ifndef PACK_STRUCT_FIELD +#define PACK_STRUCT_FIELD(x) x +#endif /* PACK_STRUCT_FIELD */ + + +#ifndef LWIP_UNUSED_ARG +#define LWIP_UNUSED_ARG(x) (void)x +#endif /* LWIP_UNUSED_ARG */ + + +#ifdef LWIP_PROVIDE_ERRNO + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ + + +#define ENSROK 0 /* DNS server returned answer with no data */ +#define ENSRNODATA 160 /* DNS server returned answer with no data */ +#define ENSRFORMERR 161 /* DNS server claims query was misformatted */ +#define ENSRSERVFAIL 162 /* DNS server returned general failure */ +#define ENSRNOTFOUND 163 /* Domain name not found */ +#define ENSRNOTIMP 164 /* DNS server does not implement requested operation */ +#define ENSRREFUSED 165 /* DNS server refused query */ +#define ENSRBADQUERY 166 /* Misformatted DNS query */ +#define ENSRBADNAME 167 /* Misformatted domain name */ +#define ENSRBADFAMILY 168 /* Unsupported address family */ +#define ENSRBADRESP 169 /* Misformatted DNS reply */ +#define ENSRCONNREFUSED 170 /* Could not contact DNS servers */ +#define ENSRTIMEOUT 171 /* Timeout while contacting DNS servers */ +#define ENSROF 172 /* End of file */ +#define ENSRFILE 173 /* Error reading file */ +#define ENSRNOMEM 174 /* Out of memory */ +#define ENSRDESTRUCTION 175 /* Application terminated lookup */ +#define ENSRQUERYDOMAINTOOLONG 176 /* Domain name is too long */ +#define ENSRCNAMELOOP 177 /* Domain name is too long */ + +#ifndef errno +extern int errno; +#endif + +#endif /* LWIP_PROVIDE_ERRNO */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ARCH_H__ */ diff --git a/app/include/lwip/autoip.h b/app/include/lwip/autoip.h new file mode 100644 index 00000000..23c264a1 --- /dev/null +++ b/app/include/lwip/autoip.h @@ -0,0 +1,119 @@ +/** + * @file + * + * AutoIP Automatic LinkLocal IP Configuration + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +#ifndef __LWIP_AUTOIP_H__ +#define __LWIP_AUTOIP_H__ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +#include "lwip/udp.h" +#include "netif/etharp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* AutoIP Timing */ +#define AUTOIP_TMR_INTERVAL 100 +#define AUTOIP_TICKS_PER_SECOND (1000 / AUTOIP_TMR_INTERVAL) + +/* RFC 3927 Constants */ +#define PROBE_WAIT 1 /* second (initial random delay) */ +#define PROBE_MIN 1 /* second (minimum delay till repeated probe) */ +#define PROBE_MAX 2 /* seconds (maximum delay till repeated probe) */ +#define PROBE_NUM 3 /* (number of probe packets) */ +#define ANNOUNCE_NUM 2 /* (number of announcement packets) */ +#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */ +#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */ +#define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */ +#define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */ +#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */ + +/* AutoIP client states */ +#define AUTOIP_STATE_OFF 0 +#define AUTOIP_STATE_PROBING 1 +#define AUTOIP_STATE_ANNOUNCING 2 +#define AUTOIP_STATE_BOUND 3 + +struct autoip +{ + ip_addr_t llipaddr; /* the currently selected, probed, announced or used LL IP-Address */ + u8_t state; /* current AutoIP state machine state */ + u8_t sent_num; /* sent number of probes or announces, dependent on state */ + u16_t ttw; /* ticks to wait, tick is AUTOIP_TMR_INTERVAL long */ + u8_t lastconflict; /* ticks until a conflict can be solved by defending */ + u8_t tried_llipaddr; /* total number of probed/used Link Local IP-Addresses */ +}; + + +/** Init srand, has to be called before entering mainloop */ +void autoip_init(void); + +/** Set a struct autoip allocated by the application to work with */ +void autoip_set_struct(struct netif *netif, struct autoip *autoip); + +/** Start AutoIP client */ +err_t autoip_start(struct netif *netif); + +/** Stop AutoIP client */ +err_t autoip_stop(struct netif *netif); + +/** Handles every incoming ARP Packet, called by etharp_arp_input */ +void autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr); + +/** Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds */ +void autoip_tmr(void); + +/** Handle a possible change in the network configuration */ +void autoip_network_changed(struct netif *netif); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_AUTOIP */ + +#endif /* __LWIP_AUTOIP_H__ */ diff --git a/app/include/lwip/debug.h b/app/include/lwip/debug.h new file mode 100644 index 00000000..d8359ea3 --- /dev/null +++ b/app/include/lwip/debug.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_DEBUG_H__ +#define __LWIP_DEBUG_H__ + +#include "lwip/arch.h" + +/** lower two bits indicate debug level + * - 0 all + * - 1 warning + * - 2 serious + * - 3 severe + */ +#define LWIP_DBG_LEVEL_ALL 0x00 +#define LWIP_DBG_LEVEL_OFF LWIP_DBG_LEVEL_ALL /* compatibility define only */ +#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */ +#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */ +#define LWIP_DBG_LEVEL_SEVERE 0x03 +#define LWIP_DBG_MASK_LEVEL 0x03 + +/** flag for LWIP_DEBUGF to enable that debug message */ +#define LWIP_DBG_ON 0x80U +/** flag for LWIP_DEBUGF to disable that debug message */ +#define LWIP_DBG_OFF 0x00U + +/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */ +#define LWIP_DBG_TRACE 0x40U +/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */ +#define LWIP_DBG_STATE 0x20U +/** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */ +#define LWIP_DBG_FRESH 0x10U +/** flag for LWIP_DEBUGF to halt after printing this debug message */ +#define LWIP_DBG_HALT 0x08U + +#ifndef LWIP_NOASSERT +#define LWIP_ASSERT(message, assertion) do { if(!(assertion)) \ + LWIP_PLATFORM_ASSERT(message); } while(0) +#else /* LWIP_NOASSERT */ +#define LWIP_ASSERT(message, assertion) +#endif /* LWIP_NOASSERT */ + +/** if "expression" isn't true, then print "message" and execute "handler" expression */ +#ifndef LWIP_ERROR +#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ + LWIP_PLATFORM_ASSERT(message); handler;}} while(0) +#endif /* LWIP_ERROR */ + +#ifdef LWIP_DEBUG +/** print debug message only if debug message type is enabled... + * AND is of correct type AND is at least LWIP_DBG_LEVEL + */ +#define LWIP_DEBUGF(debug, message) do { \ + if ( \ + ((debug) & LWIP_DBG_ON) && \ + ((debug) & LWIP_DBG_TYPES_ON) && \ + ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \ + LWIP_PLATFORM_DIAG(message); \ + if ((debug) & LWIP_DBG_HALT) { \ + while(1); \ + } \ + } \ + } while(0) + +#else /* LWIP_DEBUG */ +#define LWIP_DEBUGF(debug, message) +#endif /* LWIP_DEBUG */ + +#endif /* __LWIP_DEBUG_H__ */ + diff --git a/app/include/lwip/def.h b/app/include/lwip/def.h new file mode 100644 index 00000000..9b6de6a8 --- /dev/null +++ b/app/include/lwip/def.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_DEF_H__ +#define __LWIP_DEF_H__ + +/* arch.h might define NULL already */ +#include "lwip/arch.h" +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LWIP_MAX(x , y) (((x) > (y)) ? (x) : (y)) +#define LWIP_MIN(x , y) (((x) < (y)) ? (x) : (y)) + +#ifndef NULL +#define NULL ((void *)0) +#endif + +/** Get the absolute difference between 2 u32_t values (correcting overflows) + * 'a' is expected to be 'higher' (without overflow) than 'b'. */ +#define LWIP_U32_DIFF(a, b) (((a) >= (b)) ? ((a) - (b)) : (((a) + ((b) ^ 0xFFFFFFFF) + 1))) + +/* Endianess-optimized shifting of two u8_t to create one u16_t */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define LWIP_MAKE_U16(a, b) ((a << 8) | b) +#else +#define LWIP_MAKE_U16(a, b) ((b << 8) | a) +#endif + +#ifndef LWIP_PLATFORM_BYTESWAP +#define LWIP_PLATFORM_BYTESWAP 0 +#endif + +#ifndef LWIP_PREFIX_BYTEORDER_FUNCS +/* workaround for naming collisions on some platforms */ + +#ifdef htons +#undef htons +#endif /* htons */ +#ifdef htonl +#undef htonl +#endif /* htonl */ +#ifdef ntohs +#undef ntohs +#endif /* ntohs */ +#ifdef ntohl +#undef ntohl +#endif /* ntohl */ + +#define htons(x) lwip_htons(x) +#define ntohs(x) lwip_ntohs(x) +#define htonl(x) lwip_htonl(x) +#define ntohl(x) lwip_ntohl(x) +#endif /* LWIP_PREFIX_BYTEORDER_FUNCS */ + +#if BYTE_ORDER == BIG_ENDIAN +#define lwip_htons(x) (x) +#define lwip_ntohs(x) (x) +#define lwip_htonl(x) (x) +#define lwip_ntohl(x) (x) +#define PP_HTONS(x) (x) +#define PP_NTOHS(x) (x) +#define PP_HTONL(x) (x) +#define PP_NTOHL(x) (x) +#else /* BYTE_ORDER != BIG_ENDIAN */ +#if LWIP_PLATFORM_BYTESWAP +#define lwip_htons(x) LWIP_PLATFORM_HTONS(x) +#define lwip_ntohs(x) LWIP_PLATFORM_HTONS(x) +#define lwip_htonl(x) LWIP_PLATFORM_HTONL(x) +#define lwip_ntohl(x) LWIP_PLATFORM_HTONL(x) +#else /* LWIP_PLATFORM_BYTESWAP */ +u16_t lwip_htons(u16_t x); +u16_t lwip_ntohs(u16_t x); +u32_t lwip_htonl(u32_t x); +u32_t lwip_ntohl(u32_t x); +#endif /* LWIP_PLATFORM_BYTESWAP */ + +/* These macros should be calculated by the preprocessor and are used + with compile-time constants only (so that there is no little-endian + overhead at runtime). */ +#define PP_HTONS(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8)) +#define PP_NTOHS(x) PP_HTONS(x) +#define PP_HTONL(x) ((((x) & 0xff) << 24) | \ + (((x) & 0xff00) << 8) | \ + (((x) & 0xff0000UL) >> 8) | \ + (((x) & 0xff000000UL) >> 24)) +#define PP_NTOHL(x) PP_HTONL(x) + +#endif /* BYTE_ORDER == BIG_ENDIAN */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_DEF_H__ */ + diff --git a/app/include/lwip/dhcp.h b/app/include/lwip/dhcp.h new file mode 100644 index 00000000..ba21068c --- /dev/null +++ b/app/include/lwip/dhcp.h @@ -0,0 +1,243 @@ +/** @file + */ + +#ifndef __LWIP_DHCP_H__ +#define __LWIP_DHCP_H__ + +#include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +#include "lwip/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** period (in seconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_SECS 60 +/** period (in milliseconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_MSECS (DHCP_COARSE_TIMER_SECS * 1000UL) +/** period (in milliseconds) of the application calling dhcp_fine_tmr() */ +#define DHCP_FINE_TIMER_MSECS 500 + +#define DHCP_CHADDR_LEN 16U +#define DHCP_SNAME_LEN 64U +#define DHCP_FILE_LEN 128U + +struct dhcp +{ + /** transaction identifier of last sent request */ + u32_t xid; + /** our connection to the DHCP server */ + struct udp_pcb *pcb; + /** incoming msg */ + struct dhcp_msg *msg_in; + /** current DHCP state machine state */ + u8_t state; + /** retries of current request */ + u8_t tries; +#if LWIP_DHCP_AUTOIP_COOP + u8_t autoip_coop_state; +#endif + u8_t subnet_mask_given; + + struct pbuf *p_out; /* pbuf of outcoming msg */ + struct dhcp_msg *msg_out; /* outgoing msg */ + u16_t options_out_len; /* outgoing msg options length */ + u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */ + u16_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */ + u16_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */ + ip_addr_t server_ip_addr; /* dhcp server address that offered this lease */ + ip_addr_t offered_ip_addr; + ip_addr_t offered_sn_mask; + ip_addr_t offered_gw_addr; + + u32_t offered_t0_lease; /* lease period (in seconds) */ + u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */ + u32_t offered_t2_rebind; /* recommended rebind time (usually 66% of lease period) */ + /* @todo: LWIP_DHCP_BOOTP_FILE configuration option? + integrate with possible TFTP-client for booting? */ +#define LWIP_DHCP_BOOTP_FILE 0 +#if LWIP_DHCP_BOOTP_FILE + ip_addr_t offered_si_addr; + char boot_file_name[DHCP_FILE_LEN]; +#endif /* LWIP_DHCP_BOOTPFILE */ +}; + +/* MUST be compiled with "pack structs" or equivalent! */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** minimum set of fields of any DHCP message */ +struct dhcp_msg +{ + PACK_STRUCT_FIELD(u8_t op); + PACK_STRUCT_FIELD(u8_t htype); + PACK_STRUCT_FIELD(u8_t hlen); + PACK_STRUCT_FIELD(u8_t hops); + PACK_STRUCT_FIELD(u32_t xid); + PACK_STRUCT_FIELD(u16_t secs); + PACK_STRUCT_FIELD(u16_t flags); + PACK_STRUCT_FIELD(ip_addr_p_t ciaddr); + PACK_STRUCT_FIELD(ip_addr_p_t yiaddr); + PACK_STRUCT_FIELD(ip_addr_p_t siaddr); + PACK_STRUCT_FIELD(ip_addr_p_t giaddr); + PACK_STRUCT_FIELD(u8_t chaddr[DHCP_CHADDR_LEN]); + PACK_STRUCT_FIELD(u8_t sname[DHCP_SNAME_LEN]); + PACK_STRUCT_FIELD(u8_t file[DHCP_FILE_LEN]); + PACK_STRUCT_FIELD(u32_t cookie); +#define DHCP_MIN_OPTIONS_LEN 68U +/** make sure user does not configure this too small */ +#if ((defined(DHCP_OPTIONS_LEN)) && (DHCP_OPTIONS_LEN < DHCP_MIN_OPTIONS_LEN)) +# undef DHCP_OPTIONS_LEN +#endif +/** allow this to be configured in lwipopts.h, but not too small */ +#if (!defined(DHCP_OPTIONS_LEN)) +/** set this to be sufficient for your options in outgoing DHCP msgs */ +# define DHCP_OPTIONS_LEN DHCP_MIN_OPTIONS_LEN +#endif + PACK_STRUCT_FIELD(u8_t options[DHCP_OPTIONS_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +void dhcp_set_struct(struct netif *netif, struct dhcp *dhcp); +/** Remove a struct dhcp previously set to the netif using dhcp_set_struct() */ +#define dhcp_remove_struct(netif) do { (netif)->dhcp = NULL; } while(0) +void dhcp_cleanup(struct netif *netif); +/** start DHCP configuration */ +err_t dhcp_start(struct netif *netif); +/** enforce early lease renewal (not needed normally)*/ +err_t dhcp_renew(struct netif *netif); +/** release the DHCP lease, usually called before dhcp_stop()*/ +err_t dhcp_release(struct netif *netif); +/** stop DHCP configuration */ +void dhcp_stop(struct netif *netif); +/** inform server of our manual IP address */ +void dhcp_inform(struct netif *netif); +/** Handle a possible change in the network configuration */ +void dhcp_network_changed(struct netif *netif); + +/** if enabled, check whether the offered IP address is not in use, using ARP */ +#if DHCP_DOES_ARP_CHECK +void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr); +#endif + +/** to be called every minute */ +void dhcp_coarse_tmr(void); +/** to be called every half second */ +void dhcp_fine_tmr(void); + +/** DHCP message item offsets and length */ +#define DHCP_OP_OFS 0 +#define DHCP_HTYPE_OFS 1 +#define DHCP_HLEN_OFS 2 +#define DHCP_HOPS_OFS 3 +#define DHCP_XID_OFS 4 +#define DHCP_SECS_OFS 8 +#define DHCP_FLAGS_OFS 10 +#define DHCP_CIADDR_OFS 12 +#define DHCP_YIADDR_OFS 16 +#define DHCP_SIADDR_OFS 20 +#define DHCP_GIADDR_OFS 24 +#define DHCP_CHADDR_OFS 28 +#define DHCP_SNAME_OFS 44 +#define DHCP_FILE_OFS 108 +#define DHCP_MSG_LEN 236 + +#define DHCP_COOKIE_OFS DHCP_MSG_LEN +#define DHCP_OPTIONS_OFS (DHCP_MSG_LEN + 4) + +#define DHCP_CLIENT_PORT 68 +#define DHCP_SERVER_PORT 67 + +/** DHCP client states */ +#define DHCP_OFF 0 +#define DHCP_REQUESTING 1 +#define DHCP_INIT 2 +#define DHCP_REBOOTING 3 +#define DHCP_REBINDING 4 +#define DHCP_RENEWING 5 +#define DHCP_SELECTING 6 +#define DHCP_INFORMING 7 +#define DHCP_CHECKING 8 +#define DHCP_PERMANENT 9 +#define DHCP_BOUND 10 +/** not yet implemented #define DHCP_RELEASING 11 */ +#define DHCP_BACKING_OFF 12 + +/** AUTOIP cooperatation flags */ +#define DHCP_AUTOIP_COOP_STATE_OFF 0 +#define DHCP_AUTOIP_COOP_STATE_ON 1 + +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 + +/** DHCP message types */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 + +/** DHCP hardware type, currently only ethernet is supported */ +#define DHCP_HTYPE_ETH 1 + +#define DHCP_MAGIC_COOKIE 0x63825363UL + +/* This is a list of options for BOOTP and DHCP, see RFC 2132 for descriptions */ + +/** BootP options */ +#define DHCP_OPTION_PAD 0 +#define DHCP_OPTION_SUBNET_MASK 1 /* RFC 2132 3.3 */ +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DNS_SERVER 6 +#define DHCP_OPTION_HOSTNAME 12 +#define DHCP_OPTION_IP_TTL 23 +#define DHCP_OPTION_MTU 26 +#define DHCP_OPTION_BROADCAST 28 +#define DHCP_OPTION_TCP_TTL 37 +#define DHCP_OPTION_END 255 + +/** DHCP options */ +#define DHCP_OPTION_REQUESTED_IP 50 /* RFC 2132 9.1, requested IP address */ +#define DHCP_OPTION_LEASE_TIME 51 /* RFC 2132 9.2, time in seconds, in 4 bytes */ +#define DHCP_OPTION_OVERLOAD 52 /* RFC2132 9.3, use file and/or sname field for options */ + +#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */ +#define DHCP_OPTION_MESSAGE_TYPE_LEN 1 + +#define DHCP_OPTION_SERVER_ID 54 /* RFC 2132 9.7, server IP address */ +#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* RFC 2132 9.8, requested option types */ + +#define DHCP_OPTION_MAX_MSG_SIZE 57 /* RFC 2132 9.10, message size accepted >= 576 */ +#define DHCP_OPTION_MAX_MSG_SIZE_LEN 2 + +#define DHCP_OPTION_T1 58 /* T1 renewal time */ +#define DHCP_OPTION_T2 59 /* T2 rebinding time */ +#define DHCP_OPTION_US 60 +#define DHCP_OPTION_CLIENT_ID 61 +#define DHCP_OPTION_TFTP_SERVERNAME 66 +#define DHCP_OPTION_BOOTFILE 67 + +/** possible combinations of overloading the file and sname fields with options */ +#define DHCP_OVERLOAD_NONE 0 +#define DHCP_OVERLOAD_FILE 1 +#define DHCP_OVERLOAD_SNAME 2 +#define DHCP_OVERLOAD_SNAME_FILE 3 + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DHCP */ + +#endif /*__LWIP_DHCP_H__*/ diff --git a/app/include/lwip/dns.h b/app/include/lwip/dns.h new file mode 100644 index 00000000..6c7d9b07 --- /dev/null +++ b/app/include/lwip/dns.h @@ -0,0 +1,124 @@ +/** + * lwip DNS resolver header file. + + * Author: Jim Pettinato + * April 2007 + + * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __LWIP_DNS_H__ +#define __LWIP_DNS_H__ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** DNS timer period */ +#define DNS_TMR_INTERVAL 1000 + +/** DNS field TYPE used for "Resource Records" */ +#define DNS_RRTYPE_A 1 /* a host address */ +#define DNS_RRTYPE_NS 2 /* an authoritative name server */ +#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */ +#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */ +#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */ +#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */ +#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */ +#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */ +#define DNS_RRTYPE_WKS 11 /* a well known service description */ +#define DNS_RRTYPE_PTR 12 /* a domain name pointer */ +#define DNS_RRTYPE_HINFO 13 /* host information */ +#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */ +#define DNS_RRTYPE_MX 15 /* mail exchange */ +#define DNS_RRTYPE_TXT 16 /* text strings */ + +/** DNS field CLASS used for "Resource Records" */ +#define DNS_RRCLASS_IN 1 /* the Internet */ +#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */ +#define DNS_RRCLASS_CH 3 /* the CHAOS class */ +#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */ +#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */ + +/* The size used for the next line is rather a hack, but it prevents including socket.h in all files + that include memp.h, and that would possibly break portability (since socket.h defines some types + and constants possibly already define by the OS). + Calculation rule: + sizeof(struct addrinfo) + sizeof(struct sockaddr_in) + DNS_MAX_NAME_LENGTH + 1 byte zero-termination */ +#define NETDB_ELEM_SIZE (32 + 16 + DNS_MAX_NAME_LENGTH + 1) + +#if DNS_LOCAL_HOSTLIST +/** struct used for local host-list */ +struct local_hostlist_entry { + /** static hostname */ + const char *name; + /** static host address in network byteorder */ + ip_addr_t addr; + struct local_hostlist_entry *next; +}; +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +#ifndef DNS_LOCAL_HOSTLIST_MAX_NAMELEN +#define DNS_LOCAL_HOSTLIST_MAX_NAMELEN DNS_MAX_NAME_LENGTH +#endif +#define LOCALHOSTLIST_ELEM_SIZE ((sizeof(struct local_hostlist_entry) + DNS_LOCAL_HOSTLIST_MAX_NAMELEN + 1)) +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ +#endif /* DNS_LOCAL_HOSTLIST */ + +/** Callback which is invoked when a hostname is found. + * A function of this type must be implemented by the application using the DNS resolver. + * @param name pointer to the name that was looked up. + * @param ipaddr pointer to an ip_addr_t containing the IP address of the hostname, + * or NULL if the name could not be found (or on any other error). + * @param callback_arg a user-specified callback argument passed to dns_gethostbyname +*/ +typedef void (*dns_found_callback)(const char *name, ip_addr_t *ipaddr, void *callback_arg); + +void dns_init(void); +void dns_tmr(void); +void dns_setserver(u8_t numdns, ip_addr_t *dnsserver); +ip_addr_t dns_getserver(u8_t numdns); +err_t dns_gethostbyname(const char *hostname, ip_addr_t *addr, + dns_found_callback found, void *callback_arg); + +#if DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC +int dns_local_removehost(const char *hostname, const ip_addr_t *addr); +err_t dns_local_addhost(const char *hostname, const ip_addr_t *addr); +#endif /* DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DNS */ + +#endif /* __LWIP_DNS_H__ */ diff --git a/app/include/lwip/err.h b/app/include/lwip/err.h new file mode 100644 index 00000000..cb69a9f4 --- /dev/null +++ b/app/include/lwip/err.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ERR_H__ +#define __LWIP_ERR_H__ + +#include "lwip/opt.h" +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Define LWIP_ERR_T in cc.h if you want to use + * a different type for your platform (must be signed). */ +#ifdef LWIP_ERR_T +typedef LWIP_ERR_T err_t; +#else /* LWIP_ERR_T */ +typedef s8_t err_t; +#endif /* LWIP_ERR_T*/ + +/* Definitions for error constants. */ + +#define ERR_OK 0 /* No error, everything OK. */ +#define ERR_MEM -1 /* Out of memory error. */ +#define ERR_BUF -2 /* Buffer error. */ +#define ERR_TIMEOUT -3 /* Timeout. */ +#define ERR_RTE -4 /* Routing problem. */ +#define ERR_INPROGRESS -5 /* Operation in progress */ +#define ERR_VAL -6 /* Illegal value. */ +#define ERR_WOULDBLOCK -7 /* Operation would block. */ + +#define ERR_IS_FATAL(e) ((e) < ERR_WOULDBLOCK) + +#define ERR_ABRT -8 /* Connection aborted. */ +#define ERR_RST -9 /* Connection reset. */ +#define ERR_CLSD -10 /* Connection closed. */ +#define ERR_CONN -11 /* Not connected. */ + +#define ERR_ARG -12 /* Illegal argument. */ + +#define ERR_USE -13 /* Address in use. */ + +#define ERR_IF -14 /* Low-level netif error */ +#define ERR_ISCONN -15 /* Already connected. */ + + +#ifdef LWIP_DEBUG +extern const char *lwip_strerr(err_t err)ICACHE_FLASH_ATTR; +#else +#define lwip_strerr(x) "" +#endif /* LWIP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ERR_H__ */ diff --git a/app/include/lwip/icmp.h b/app/include/lwip/icmp.h new file mode 100644 index 00000000..9bcb7bc4 --- /dev/null +++ b/app/include/lwip/icmp.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ICMP_H__ +#define __LWIP_ICMP_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ICMP_ER 0 /* echo reply */ +#define ICMP_DUR 3 /* destination unreachable */ +#define ICMP_SQ 4 /* source quench */ +#define ICMP_RD 5 /* redirect */ +#define ICMP_ECHO 8 /* echo */ +#define ICMP_TE 11 /* time exceeded */ +#define ICMP_PP 12 /* parameter problem */ +#define ICMP_TS 13 /* timestamp */ +#define ICMP_TSR 14 /* timestamp reply */ +#define ICMP_IRQ 15 /* information request */ +#define ICMP_IR 16 /* information reply */ + +enum icmp_dur_type { + ICMP_DUR_NET = 0, /* net unreachable */ + ICMP_DUR_HOST = 1, /* host unreachable */ + ICMP_DUR_PROTO = 2, /* protocol unreachable */ + ICMP_DUR_PORT = 3, /* port unreachable */ + ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */ + ICMP_DUR_SR = 5 /* source route failed */ +}; + +enum icmp_te_type { + ICMP_TE_TTL = 0, /* time to live exceeded in transit */ + ICMP_TE_FRAG = 1 /* fragment reassembly time exceeded */ +}; + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +/** This is the standard ICMP header only that the u32_t data + * is splitted to two u16_t like ICMP echo needs it. + * This header is also used for other ICMP types that do not + * use the data part. + * ICMPײṹ + * ICMPײкܴԣ + * ýṹͬICMPġ + */ +PACK_STRUCT_BEGIN +struct icmp_echo_hdr { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u16_t seqno); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +//ȡICMPײֶ +#define ICMPH_TYPE(hdr) ((hdr)->type) +#define ICMPH_CODE(hdr) ((hdr)->code) + +/** Combines type and code to an u16_t ICMPײֶдӦֵ*/ +#define ICMPH_TYPE_SET(hdr, t) ((hdr)->type = (t)) +#define ICMPH_CODE_SET(hdr, c) ((hdr)->code = (c)) + + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +void icmp_input(struct pbuf *p, struct netif *inp)ICACHE_FLASH_ATTR; +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)ICACHE_FLASH_ATTR; +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)ICACHE_FLASH_ATTR; + +#endif /* LWIP_ICMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ICMP_H__ */ diff --git a/app/include/lwip/igmp.h b/app/include/lwip/igmp.h new file mode 100644 index 00000000..8cf9a481 --- /dev/null +++ b/app/include/lwip/igmp.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +#ifndef __LWIP_IGMP_H__ +#define __LWIP_IGMP_H__ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/pbuf.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* IGMP timer */ +#define IGMP_TMR_INTERVAL 100 /* Milliseconds */ +#define IGMP_V1_DELAYING_MEMBER_TMR (1000/IGMP_TMR_INTERVAL) +#define IGMP_JOIN_DELAYING_MEMBER_TMR (500 /IGMP_TMR_INTERVAL) + +/* MAC Filter Actions, these are passed to a netif's + * igmp_mac_filter callback function. */ +#define IGMP_DEL_MAC_FILTER 0 +#define IGMP_ADD_MAC_FILTER 1 + + +/** + * igmp group structure - there is + * a list of groups for each interface + * these should really be linked from the interface, but + * if we keep them separate we will not affect the lwip original code + * too much + * + * There will be a group for the all systems group address but this + * will not run the state machine as it is used to kick off reports + * from all the other groups + */ +struct igmp_group { + /** next link */ + struct igmp_group *next; + /** interface on which the group is active */ + struct netif *netif; + /** multicast address */ + ip_addr_t group_address; + /** signifies we were the last person to report */ + u8_t last_reporter_flag; + /** current state of the group */ + u8_t group_state; + /** timer for reporting, negative is OFF */ + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; +}; + +/* Prototypes */ +void igmp_init(void)ICACHE_FLASH_ATTR; +err_t igmp_start(struct netif *netif)ICACHE_FLASH_ATTR; +err_t igmp_stop(struct netif *netif)ICACHE_FLASH_ATTR; +void igmp_report_groups(struct netif *netif)ICACHE_FLASH_ATTR; +struct igmp_group *igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)ICACHE_FLASH_ATTR; +void igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)ICACHE_FLASH_ATTR; +err_t igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)ICACHE_FLASH_ATTR; +err_t igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)ICACHE_FLASH_ATTR; +void igmp_tmr(void)ICACHE_FLASH_ATTR; +#define LWIP_RAND() rand() +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IGMP */ + +#endif /* __LWIP_IGMP_H__ */ diff --git a/app/include/lwip/inet.h b/app/include/lwip/inet.h new file mode 100644 index 00000000..7bff49b5 --- /dev/null +++ b/app/include/lwip/inet.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INET_H__ +#define __LWIP_INET_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** For compatibility with BSD code */ +struct in_addr { + u32_t s_addr; +}; + +/** 255.255.255.255 */ +#define INADDR_NONE IPADDR_NONE +/** 127.0.0.1 */ +#define INADDR_LOOPBACK IPADDR_LOOPBACK +/** 0.0.0.0 */ +#define INADDR_ANY IPADDR_ANY +/** 255.255.255.255 */ +#define INADDR_BROADCAST IPADDR_BROADCAST + +/* Definitions of the bits in an Internet address integer. + + On subnets, host and network parts are found according to + the subnet mask, not these masks. */ +#define IN_CLASSA(a) IP_CLASSA(a) +#define IN_CLASSA_NET IP_CLASSA_NET +#define IN_CLASSA_NSHIFT IP_CLASSA_NSHIFT +#define IN_CLASSA_HOST IP_CLASSA_HOST +#define IN_CLASSA_MAX IP_CLASSA_MAX + +#define IN_CLASSB(b) IP_CLASSB(b) +#define IN_CLASSB_NET IP_CLASSB_NET +#define IN_CLASSB_NSHIFT IP_CLASSB_NSHIFT +#define IN_CLASSB_HOST IP_CLASSB_HOST +#define IN_CLASSB_MAX IP_CLASSB_MAX + +#define IN_CLASSC(c) IP_CLASSC(c) +#define IN_CLASSC_NET IP_CLASSC_NET +#define IN_CLASSC_NSHIFT IP_CLASSC_NSHIFT +#define IN_CLASSC_HOST IP_CLASSC_HOST +#define IN_CLASSC_MAX IP_CLASSC_MAX + +#define IN_CLASSD(d) IP_CLASSD(d) +#define IN_CLASSD_NET IP_CLASSD_NET /* These ones aren't really */ +#define IN_CLASSD_NSHIFT IP_CLASSD_NSHIFT /* net and host fields, but */ +#define IN_CLASSD_HOST IP_CLASSD_HOST /* routing needn't know. */ +#define IN_CLASSD_MAX IP_CLASSD_MAX + +#define IN_MULTICAST(a) IP_MULTICAST(a) + +#define IN_EXPERIMENTAL(a) IP_EXPERIMENTAL(a) +#define IN_BADCLASS(a) IP_BADCLASS(a) + +#define IN_LOOPBACKNET IP_LOOPBACKNET + +#define inet_addr_from_ipaddr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr)) +#define inet_addr_to_ipaddr(target_ipaddr, source_inaddr) (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr)) +/* ATTENTION: the next define only works because both s_addr and ip_addr_t are an u32_t effectively! */ +#define inet_addr_to_ipaddr_p(target_ipaddr_p, source_inaddr) ((target_ipaddr_p) = (ip_addr_t*)&((source_inaddr)->s_addr)) + +/* directly map this to the lwip internal functions */ +#define inet_addr(cp) ipaddr_addr(cp) +#define inet_aton(cp, addr) ipaddr_aton(cp, (ip_addr_t*)addr) +#define inet_ntoa(addr) ipaddr_ntoa((ip_addr_t*)&(addr)) +#define inet_ntoa_r(addr, buf, buflen) ipaddr_ntoa_r((ip_addr_t*)&(addr), buf, buflen) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ diff --git a/app/include/lwip/inet_chksum.h b/app/include/lwip/inet_chksum.h new file mode 100644 index 00000000..41be6415 --- /dev/null +++ b/app/include/lwip/inet_chksum.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INET_CHKSUM_H__ +#define __LWIP_INET_CHKSUM_H__ + +#include "lwip/opt.h" + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +/** Swap the bytes in an u16_t: much like htons() for little-endian */ +#ifndef SWAP_BYTES_IN_WORD +#if LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) +/* little endian and PLATFORM_BYTESWAP defined */ +#define SWAP_BYTES_IN_WORD(w) LWIP_PLATFORM_HTONS(w) +#else /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) */ +/* can't use htons on big endian (or PLATFORM_BYTESWAP not defined)... */ +#define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8) +#endif /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN)*/ +#endif /* SWAP_BYTES_IN_WORD */ + +/** Split an u32_t in two u16_ts and add them up */ +#ifndef FOLD_U32T +#define FOLD_U32T(u) (((u) >> 16) + ((u) & 0x0000ffffUL)) +#endif + +#if LWIP_CHECKSUM_ON_COPY +/** Function-like macro: same as MEMCPY but returns the checksum of copied data + as u16_t */ +#ifndef LWIP_CHKSUM_COPY +#define LWIP_CHKSUM_COPY(dst, src, len) lwip_chksum_copy(dst, src, len) +#ifndef LWIP_CHKSUM_COPY_ALGORITHM +#define LWIP_CHKSUM_COPY_ALGORITHM 1 +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ +#endif /* LWIP_CHKSUM_COPY */ +#else /* LWIP_CHECKSUM_ON_COPY */ +#define LWIP_CHKSUM_COPY_ALGORITHM 0 +#endif /* LWIP_CHECKSUM_ON_COPY */ + +#ifdef __cplusplus +extern "C" { +#endif + +u16_t inet_chksum(void *dataptr, u16_t len)ICACHE_FLASH_ATTR; +u16_t inet_chksum_pbuf(struct pbuf *p)ICACHE_FLASH_ATTR; +u16_t inet_chksum_pseudo(struct pbuf *p, + ip_addr_t *src, ip_addr_t *dest, + u8_t proto, u16_t proto_len)ICACHE_FLASH_ATTR; +u16_t inet_chksum_pseudo_partial(struct pbuf *p, + ip_addr_t *src, ip_addr_t *dest, + u8_t proto, u16_t proto_len, u16_t chksum_len)ICACHE_FLASH_ATTR; +#if LWIP_CHKSUM_COPY_ALGORITHM +u16_t lwip_chksum_copy(void *dst, const void *src, u16_t len)ICACHE_FLASH_ATTR; +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ + diff --git a/app/include/lwip/init.h b/app/include/lwip/init.h new file mode 100644 index 00000000..7a58aece --- /dev/null +++ b/app/include/lwip/init.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INIT_H__ +#define __LWIP_INIT_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** X.x.x: Major version of the stack */ +#define LWIP_VERSION_MAJOR 1U +/** x.X.x: Minor version of the stack */ +#define LWIP_VERSION_MINOR 4U +/** x.x.X: Revision of the stack */ +#define LWIP_VERSION_REVISION 0U +/** For release candidates, this is set to 1..254 + * For official releases, this is set to 255 (LWIP_RC_RELEASE) + * For development versions (CVS), this is set to 0 (LWIP_RC_DEVELOPMENT) */ +#define LWIP_VERSION_RC 2U + +/** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */ +#define LWIP_RC_RELEASE 255U +/** LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for CVS versions */ +#define LWIP_RC_DEVELOPMENT 0U + +#define LWIP_VERSION_IS_RELEASE (LWIP_VERSION_RC == LWIP_RC_RELEASE) +#define LWIP_VERSION_IS_DEVELOPMENT (LWIP_VERSION_RC == LWIP_RC_DEVELOPMENT) +#define LWIP_VERSION_IS_RC ((LWIP_VERSION_RC != LWIP_RC_RELEASE) && (LWIP_VERSION_RC != LWIP_RC_DEVELOPMENT)) + +/** Provides the version of the stack */ +#define LWIP_VERSION (LWIP_VERSION_MAJOR << 24 | LWIP_VERSION_MINOR << 16 | \ + LWIP_VERSION_REVISION << 8 | LWIP_VERSION_RC) + +/* Modules initialization */ +void lwip_init(void) ICACHE_FLASH_ATTR; +//void lwip_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INIT_H__ */ diff --git a/app/include/lwip/ip.h b/app/include/lwip/ip.h new file mode 100644 index 00000000..1f361fa4 --- /dev/null +++ b/app/include/lwip/ip.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP_H__ +#define __LWIP_IP_H__ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Currently, the function ip_output_if_opt() is only used with IGMP */ +#define IP_OPTIONS_SEND LWIP_IGMP + +#define IP_HLEN 20 + +#define IP_PROTO_ICMP 1 +#define IP_PROTO_IGMP 2 +#define IP_PROTO_UDP 17 +#define IP_PROTO_UDPLITE 136 +#define IP_PROTO_TCP 6 + +/* This is passed as the destination address to ip_output_if (not + to ip_output), meaning that an IP header already is constructed + in the pbuf. This is used when TCP retransmits. */ +#ifdef IP_HDRINCL +#undef IP_HDRINCL +#endif /* IP_HDRINCL */ +#define IP_HDRINCL NULL + +#if LWIP_NETIF_HWADDRHINT +#define IP_PCB_ADDRHINT ;u8_t addr_hint +#else +#define IP_PCB_ADDRHINT +#endif /* LWIP_NETIF_HWADDRHINT */ + +/* This is the common part of all PCB types. It needs to be at the + beginning of a PCB type definition. It is located here so that + changes to this common part are made in one location instead of + having to change all PCB structs. */ +#define IP_PCB \ + /* ip addresses in network byte order */ \ + ip_addr_t local_ip; \ + ip_addr_t remote_ip; \ + /* Socket options */ \ + u8_t so_options; \ + /* Type Of Service */ \ + u8_t tos; \ + /* Time To Live */ \ + u8_t ttl \ + /* link layer address resolution hint */ \ + IP_PCB_ADDRHINT + +struct ip_pcb { +/* Common members of all PCB types */ + IP_PCB; +}; + +/* + * Option flags per-socket. These are the same like SO_XXX. + */ +/*#define SOF_DEBUG (u8_t)0x01U Unimplemented: turn on debugging info recording */ +#define SOF_ACCEPTCONN (u8_t)0x02U /* socket has had listen() */ +#define SOF_REUSEADDR (u8_t)0x04U /* allow local address reuse */ +#define SOF_KEEPALIVE (u8_t)0x08U /* keep connections alive */ +/*#define SOF_DONTROUTE (u8_t)0x10U Unimplemented: just use interface addresses */ +#define SOF_BROADCAST (u8_t)0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +/*#define SOF_USELOOPBACK (u8_t)0x40U Unimplemented: bypass hardware when possible */ +#define SOF_LINGER (u8_t)0x80U /* linger on close if data present */ +/*#define SOF_OOBINLINE (u16_t)0x0100U Unimplemented: leave received OOB data in line */ +/*#define SOF_REUSEPORT (u16_t)0x0200U Unimplemented: allow local address & port reuse */ + +/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */ +#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE|SOF_LINGER/*|SOF_DEBUG|SOF_DONTROUTE|SOF_OOBINLINE*/) + + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_hdr { + /* version / header length / type of service */ + PACK_STRUCT_FIELD(u16_t _v_hl_tos); + /* total length */ + PACK_STRUCT_FIELD(u16_t _len); + /* identification */ + PACK_STRUCT_FIELD(u16_t _id); + /* fragment offset field */ + PACK_STRUCT_FIELD(u16_t _offset); +#define IP_RF 0x8000 /* reserved fragment flag */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + /* time to live */ + PACK_STRUCT_FIELD(u8_t _ttl); + /* protocol*/ + PACK_STRUCT_FIELD(u8_t _proto); + /* checksum */ + PACK_STRUCT_FIELD(u16_t _chksum); + /* source and destination IP addresses */ + PACK_STRUCT_FIELD(ip_addr_p_t src); + PACK_STRUCT_FIELD(ip_addr_p_t dest); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IPH_V(hdr) (ntohs((hdr)->_v_hl_tos) >> 12) +#define IPH_HL(hdr) ((ntohs((hdr)->_v_hl_tos) >> 8) & 0x0f) +#define IPH_TOS(hdr) (ntohs((hdr)->_v_hl_tos) & 0xff) +#define IPH_LEN(hdr) ((hdr)->_len) +#define IPH_ID(hdr) ((hdr)->_id) +#define IPH_OFFSET(hdr) ((hdr)->_offset) +#define IPH_TTL(hdr) ((hdr)->_ttl) +#define IPH_PROTO(hdr) ((hdr)->_proto) +#define IPH_CHKSUM(hdr) ((hdr)->_chksum) + +#define IPH_VHLTOS_SET(hdr, v, hl, tos) (hdr)->_v_hl_tos = (htons(((v) << 12) | ((hl) << 8) | (tos))) +#define IPH_LEN_SET(hdr, len) (hdr)->_len = (len) +#define IPH_ID_SET(hdr, id) (hdr)->_id = (id) +#define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off) +#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (u8_t)(ttl) +#define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto) +#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum) + +/** The interface that provided the packet for the current callback invocation. */ +extern struct netif *current_netif; +/** Header of the input packet currently being processed. */ +extern const struct ip_hdr *current_header; +/** Source IP address of current_header */ +extern ip_addr_t current_iphdr_src; +/** Destination IP address of current_header */ +extern ip_addr_t current_iphdr_dest; + +#define ip_init() /* Compatibility define, not init needed. */ +struct netif *ip_route(ip_addr_t *dest)ICACHE_FLASH_ATTR; +struct netif *ip_router(ip_addr_t *dest, ip_addr_t *source); + +err_t ip_input(struct pbuf *p, struct netif *inp)ICACHE_FLASH_ATTR; +err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto)ICACHE_FLASH_ATTR; +err_t ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, + struct netif *netif)ICACHE_FLASH_ATTR; +#if LWIP_NETIF_HWADDRHINT +err_t ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)ICACHE_FLASH_ATTR; +#endif /* LWIP_NETIF_HWADDRHINT */ +#if IP_OPTIONS_SEND +err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen)ICACHE_FLASH_ATTR; +#endif /* IP_OPTIONS_SEND */ +/** Get the interface that received the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_netif() (current_netif) +/** Get the IP header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_header() (current_header) +/** Source IP address of current_header */ +#define ip_current_src_addr() (¤t_iphdr_src) +/** Destination IP address of current_header */ +#define ip_current_dest_addr() (¤t_iphdr_dest) + +#if IP_DEBUG +void ip_debug_print(struct pbuf *p)ICACHE_FLASH_ATTR; +#else +#define ip_debug_print(p) +#endif /* IP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/app/include/lwip/ip_addr.h b/app/include/lwip/ip_addr.h new file mode 100644 index 00000000..1e46ee59 --- /dev/null +++ b/app/include/lwip/ip_addr.h @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP_ADDR_H__ +#define __LWIP_IP_ADDR_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the aligned version of ip_addr_t, + used as local variable, on the stack, etc. */ +struct ip_addr { + u32_t addr; +}; + +/* This is the packed version of ip_addr_t, + used in network headers that are itself packed */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_addr_packed { + PACK_STRUCT_FIELD(u32_t addr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** ip_addr_t uses a struct for convenience only, so that the same defines can + * operate both on ip_addr_t as well as on ip_addr_p_t. */ +typedef struct ip_addr ip_addr_t; +typedef struct ip_addr_packed ip_addr_p_t; + +/* + * struct ipaddr2 is used in the definition of the ARP packet format in + * order to support compilers that don't have structure packing. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_addr2 { + PACK_STRUCT_FIELD(u16_t addrw[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Forward declaration to not include netif.h */ +struct netif; + +extern const ip_addr_t ip_addr_any; +extern const ip_addr_t ip_addr_broadcast; + +/** IP_ADDR_ can be used as a fixed IP address + * for the wildcard and the broadcast address + */ +#define IP_ADDR_ANY ((ip_addr_t *)&ip_addr_any) +#define IP_ADDR_BROADCAST ((ip_addr_t *)&ip_addr_broadcast) + +/** 255.255.255.255 */ +#define IPADDR_NONE ((u32_t)0xffffffffUL) +/** 127.0.0.1 */ +#define IPADDR_LOOPBACK ((u32_t)0x7f000001UL) +/** 0.0.0.0 */ +#define IPADDR_ANY ((u32_t)0x00000000UL) +/** 255.255.255.255 */ +#define IPADDR_BROADCAST ((u32_t)0xffffffffUL) + +/* Definitions of the bits in an Internet address integer. + + On subnets, host and network parts are found according to + the subnet mask, not these masks. */ +#define IP_CLASSA(a) ((((u32_t)(a)) & 0x80000000UL) == 0) +#define IP_CLASSA_NET 0xff000000 +#define IP_CLASSA_NSHIFT 24 +#define IP_CLASSA_HOST (0xffffffff & ~IP_CLASSA_NET) +#define IP_CLASSA_MAX 128 + +#define IP_CLASSB(a) ((((u32_t)(a)) & 0xc0000000UL) == 0x80000000UL) +#define IP_CLASSB_NET 0xffff0000 +#define IP_CLASSB_NSHIFT 16 +#define IP_CLASSB_HOST (0xffffffff & ~IP_CLASSB_NET) +#define IP_CLASSB_MAX 65536 + +#define IP_CLASSC(a) ((((u32_t)(a)) & 0xe0000000UL) == 0xc0000000UL) +#define IP_CLASSC_NET 0xffffff00 +#define IP_CLASSC_NSHIFT 8 +#define IP_CLASSC_HOST (0xffffffff & ~IP_CLASSC_NET) + +#define IP_CLASSD(a) (((u32_t)(a) & 0xf0000000UL) == 0xe0000000UL) +#define IP_CLASSD_NET 0xf0000000 /* These ones aren't really */ +#define IP_CLASSD_NSHIFT 28 /* net and host fields, but */ +#define IP_CLASSD_HOST 0x0fffffff /* routing needn't know. */ +#define IP_MULTICAST(a) IP_CLASSD(a) + +#define IP_EXPERIMENTAL(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) +#define IP_BADCLASS(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) + +#define IP_LOOPBACKNET 127 /* official! */ + + +#if BYTE_ORDER == BIG_ENDIAN +/** Set an IP address given by the four byte-parts */ +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = ((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff) +#else +/** Set an IP address given by the four byte-parts. + Little-endian version that prevents the use of htonl. */ +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = ((u32_t)((d) & 0xff) << 24) | \ + ((u32_t)((c) & 0xff) << 16) | \ + ((u32_t)((b) & 0xff) << 8) | \ + (u32_t)((a) & 0xff) +#endif + +/** MEMCPY-like copying of IP addresses where addresses are known to be + * 16-bit-aligned if the port is correctly configured (so a port could define + * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */ +#ifndef IPADDR2_COPY +#define IPADDR2_COPY(dest, src) SMEMCPY(dest, src, sizeof(ip_addr_t)) +#endif + +/** Copy IP address - faster than ip_addr_set: no NULL check */ +#define ip_addr_copy(dest, src) ((dest).addr = (src).addr) +/** Safely copy one IP address to another (src may be NULL) */ +#define ip_addr_set(dest, src) ((dest)->addr = \ + ((src) == NULL ? 0 : \ + (src)->addr)) +/** Set complete address to zero */ +#define ip_addr_set_zero(ipaddr) ((ipaddr)->addr = 0) +/** Set address to IPADDR_ANY (no need for htonl()) */ +#define ip_addr_set_any(ipaddr) ((ipaddr)->addr = IPADDR_ANY) +/** Set address to loopback address */ +#define ip_addr_set_loopback(ipaddr) ((ipaddr)->addr = PP_HTONL(IPADDR_LOOPBACK)) +/** Safely copy one IP address to another and change byte order + * from host- to network-order. */ +#define ip_addr_set_hton(dest, src) ((dest)->addr = \ + ((src) == NULL ? 0:\ + htonl((src)->addr))) +/** IPv4 only: set the IP address given as an u32_t */ +#define ip4_addr_set_u32(dest_ipaddr, src_u32) ((dest_ipaddr)->addr = (src_u32)) +/** IPv4 only: get the IP address as an u32_t */ +#define ip4_addr_get_u32(src_ipaddr) ((src_ipaddr)->addr) + +/** Get the network address by combining host address with netmask */ +#define ip_addr_get_network(target, host, netmask) ((target)->addr = ((host)->addr) & ((netmask)->addr)) + +/** + * Determine if two address are on the same network. + * + * @arg addr1 IP address 1 + * @arg addr2 IP address 2 + * @arg mask network identifier mask + * @return !0 if the network identifiers of both address match + */ +#define ip_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \ + (mask)->addr) == \ + ((addr2)->addr & \ + (mask)->addr)) +#define ip_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr) + +#define ip_addr_isany(addr1) ((addr1) == NULL || (addr1)->addr == IPADDR_ANY) + +#define ip_addr_isbroadcast(ipaddr, netif) ip4_addr_isbroadcast((ipaddr)->addr, (netif)) +u8_t ip4_addr_isbroadcast(u32_t addr, const struct netif *netif)ICACHE_FLASH_ATTR; + +#define ip_addr_netmask_valid(netmask) ip4_addr_netmask_valid((netmask)->addr) +u8_t ip4_addr_netmask_valid(u32_t netmask)ICACHE_FLASH_ATTR; + +#define ip_addr_ismulticast(addr1) (((addr1)->addr & PP_HTONL(0xf0000000UL)) == PP_HTONL(0xe0000000UL)) + +#define ip_addr_islinklocal(addr1) (((addr1)->addr & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xa9fe0000UL)) + +#define ip_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"U16_F".%"U16_F".%"U16_F".%"U16_F, \ + ipaddr != NULL ? ip4_addr1_16(ipaddr) : 0, \ + ipaddr != NULL ? ip4_addr2_16(ipaddr) : 0, \ + ipaddr != NULL ? ip4_addr3_16(ipaddr) : 0, \ + ipaddr != NULL ? ip4_addr4_16(ipaddr) : 0)) + +/* 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)) + +/** For backwards compatibility */ +#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr) + +u32_t ipaddr_addr(const char *cp)ICACHE_FLASH_ATTR; +int ipaddr_aton(const char *cp, ip_addr_t *addr)ICACHE_FLASH_ATTR; +/** returns ptr to static buffer; not reentrant! */ +char *ipaddr_ntoa(const ip_addr_t *addr)ICACHE_FLASH_ATTR; +char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)ICACHE_FLASH_ATTR; + +#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" + +struct ip_info { + struct ip_addr ip; + struct ip_addr netmask; + struct ip_addr gw; +}; +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/app/include/lwip/ip_frag.h b/app/include/lwip/ip_frag.h new file mode 100644 index 00000000..df6db5f5 --- /dev/null +++ b/app/include/lwip/ip_frag.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * + */ + +#ifndef __LWIP_IP_FRAG_H__ +#define __LWIP_IP_FRAG_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if IP_REASSEMBLY +/* The IP reassembly timer interval in milliseconds. */ +#define IP_TMR_INTERVAL 1000 + +/* IP reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip_reassdata { + struct ip_reassdata *next; + struct pbuf *p; + struct ip_hdr iphdr; + u16_t datagram_len; + u8_t flags; + u8_t timer; +}; + +void ip_reass_init(void)ICACHE_FLASH_ATTR; +void ip_reass_tmr(void)ICACHE_FLASH_ATTR; +struct pbuf * ip_reass(struct pbuf *p)ICACHE_FLASH_ATTR; +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF +/** A custom pbuf that holds a reference to another pbuf, which is freed + * when this custom pbuf is freed. This is used to create a custom PBUF_REF + * that points into the original pbuf. */ +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */ + +err_t ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)ICACHE_FLASH_ATTR; +#endif /* IP_FRAG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_FRAG_H__ */ diff --git a/app/include/lwip/mem.h b/app/include/lwip/mem.h new file mode 100644 index 00000000..7a4febcc --- /dev/null +++ b/app/include/lwip/mem.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_MEM_H__ +#define __LWIP_MEM_H__ + +#include "lwip/opt.h" +#include "mem_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MEM_LIBC_MALLOC + +#include /* for size_t */ + +typedef size_t mem_size_t; + +/* aliases for C library malloc() */ +#define mem_init() +/* in case C library malloc() needs extra protection, + * allow these defines to be overridden. + */ +#ifndef mem_free +#define mem_free vPortFree +#endif +#ifndef mem_malloc +#define mem_malloc pvPortMalloc +#endif +#ifndef mem_calloc +#define mem_calloc pvPortCalloc +#endif +#ifndef mem_realloc +#define mem_realloc pvPortRealloc +#endif +#ifndef mem_zalloc +#define mem_zalloc pvPortZalloc +#endif + +#ifndef os_malloc +#define os_malloc(s) mem_malloc((s)) +#endif +#ifndef os_realloc +#define os_realloc(p, s) mem_realloc((p), (s)) +#endif +#ifndef os_zalloc +#define os_zalloc(s) mem_zalloc((s)) +#endif +#ifndef os_free +#define os_free(p) mem_free((p)) +#endif + +/* Since there is no C library allocation function to shrink memory without + moving it, define this to nothing. */ +#ifndef mem_trim +#define mem_trim(mem, size) (mem) +#endif +#else /* MEM_LIBC_MALLOC */ + +/* MEM_SIZE would have to be aligned, but using 64000 here instead of + * 65535 leaves some room for alignment... + */ +#if MEM_SIZE > 64000l +typedef u32_t mem_size_t; +#define MEM_SIZE_F U32_F +#else +typedef u16_t mem_size_t; +#define MEM_SIZE_F U16_F +#endif /* MEM_SIZE > 64000 */ + +#if MEM_USE_POOLS +/** mem_init is not used when using pools instead of a heap */ +#define mem_init() +/** mem_trim is not used when using pools instead of a heap: + we can't free part of a pool element and don't want to copy the rest */ +#define mem_trim(mem, size) (mem) +#else /* MEM_USE_POOLS */ +/* lwIP alternative malloc */ +void mem_init(void)ICACHE_FLASH_ATTR; +void *mem_trim(void *mem, mem_size_t size)ICACHE_FLASH_ATTR; +#endif /* MEM_USE_POOLS */ +void *mem_malloc(mem_size_t size)ICACHE_FLASH_ATTR; +void *mem_calloc(mem_size_t count, mem_size_t size)ICACHE_FLASH_ATTR; +void mem_free(void *mem)ICACHE_FLASH_ATTR; +#endif /* MEM_LIBC_MALLOC */ + +/** Calculate memory size for an aligned buffer - returns the next highest + * multiple of MEM_ALIGNMENT (e.g. LWIP_MEM_ALIGN_SIZE(3) and + * LWIP_MEM_ALIGN_SIZE(4) will both yield 4 for MEM_ALIGNMENT == 4). + */ +#ifndef LWIP_MEM_ALIGN_SIZE +#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1)) +#endif + +/** Calculate safe memory size for an aligned buffer when using an unaligned + * type as storage. This includes a safety-margin on (MEM_ALIGNMENT - 1) at the + * start (e.g. if buffer is u8_t[] and actual data will be u32_t*) + */ +#ifndef LWIP_MEM_ALIGN_BUFFER +#define LWIP_MEM_ALIGN_BUFFER(size) (((size) + MEM_ALIGNMENT - 1)) +#endif + +/** Align a memory pointer to the alignment defined by MEM_ALIGNMENT + * so that ADDR % MEM_ALIGNMENT == 0 + */ +#ifndef LWIP_MEM_ALIGN +#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1))) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEM_H__ */ diff --git a/app/include/lwip/memp.h b/app/include/lwip/memp.h new file mode 100644 index 00000000..764dedb1 --- /dev/null +++ b/app/include/lwip/memp.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __LWIP_MEMP_H__ +#define __LWIP_MEMP_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */ +typedef enum { +#define LWIP_MEMPOOL(name,num,size,desc, attr) MEMP_##name, +#include "lwip/memp_std.h" + MEMP_MAX +} memp_t; + +#if MEM_USE_POOLS +/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */ +typedef enum { + /* Get the first (via: + MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/ + MEMP_POOL_HELPER_FIRST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START 1 +#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0 +#define LWIP_MALLOC_MEMPOOL_END +#include "lwip/memp_std.h" + ) , + /* Get the last (via: + MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */ + MEMP_POOL_HELPER_LAST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size * +#define LWIP_MALLOC_MEMPOOL_END 1 +#include "lwip/memp_std.h" + ) +} memp_pool_helper_t; + +/* The actual start and stop values are here (cast them over) + We use this helper type and these defines so we can avoid using const memp_t values */ +#define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST) +#define MEMP_POOL_LAST ((memp_t) MEMP_POOL_HELPER_LAST) +#endif /* MEM_USE_POOLS */ + +#if MEMP_MEM_MALLOC || MEM_USE_POOLS +extern const u16_t memp_sizes[MEMP_MAX]; +#endif /* MEMP_MEM_MALLOC || MEM_USE_POOLS */ + +#if MEMP_MEM_MALLOC + +#include "mem.h" + +#define memp_init() +#define memp_malloc(type) mem_malloc(memp_sizes[type]) +#define memp_free(type, mem) mem_free(mem) + +#else /* MEMP_MEM_MALLOC */ + +#if MEM_USE_POOLS +/** This structure is used to save the pool one element came from. */ +struct memp_malloc_helper +{ + memp_t poolnr; +}; +#endif /* MEM_USE_POOLS */ + +void memp_init(void)ICACHE_FLASH_ATTR; + +#if MEMP_OVERFLOW_CHECK +void *memp_malloc_fn(memp_t type, const char* file, const int line)ICACHE_FLASH_ATTR; +#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__) +#else +void *memp_malloc(memp_t type)ICACHE_FLASH_ATTR; +#endif +void memp_free(memp_t type, void *mem)ICACHE_FLASH_ATTR; + +#endif /* MEMP_MEM_MALLOC */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEMP_H__ */ diff --git a/app/include/lwip/memp_std.h b/app/include/lwip/memp_std.h new file mode 100644 index 00000000..b20a2405 --- /dev/null +++ b/app/include/lwip/memp_std.h @@ -0,0 +1,126 @@ +/* + * SETUP: Make sure we define everything we will need. + * + * We have create three types of pools: + * 1) MEMPOOL - standard pools + * 2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c + * 3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct + * + * If the include'r doesn't require any special treatment of each of the types + * above, then will declare #2 & #3 to be just standard mempools. + */ +#ifndef LWIP_MALLOC_MEMPOOL +/* This treats "malloc pools" just like any other pool. + The pools are a little bigger to provide 'size' as the amount of user data. */ +#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + sizeof(struct memp_malloc_helper)), "MALLOC_"#size, attr) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL_END +#endif /* LWIP_MALLOC_MEMPOOL */ + +#ifndef LWIP_PBUF_MEMPOOL +/* This treats "pbuf pools" just like any other pool. + * Allocates buffers for a pbuf struct AND a payload size */ +#define LWIP_PBUF_MEMPOOL(name, num, payload, desc, attr) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc, attr) +#endif /* LWIP_PBUF_MEMPOOL */ + + +/* + * A list of internal pools used by LWIP. + * + * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + */ +#if LWIP_RAW +LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB", DMEM_ATTR) +#endif /* LWIP_RAW */ + +#if LWIP_UDP +LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB", DMEM_ATTR) +#endif /* LWIP_UDP */ + +#if LWIP_TCP +LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB", DMEM_ATTR) +LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN", DMEM_ATTR) +LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG", DMEM_ATTR) +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA", DMEM_ATTR) +#endif /* IP_REASSEMBLY */ +#if IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF +LWIP_MEMPOOL(FRAG_PBUF, MEMP_NUM_FRAG_PBUF, sizeof(struct pbuf_custom_ref),"FRAG_PBUF", DMEM_ATTR) +#endif /* IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */ + +#if LWIP_NETCONN +LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF") +LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN") +#endif /* LWIP_NETCONN */ + +#if NO_SYS==0 +LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API") +#if !LWIP_TCPIP_CORE_LOCKING_INPUT +LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT") +#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */ +#endif /* NO_SYS==0 */ + +#if ARP_QUEUEING +LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE", DMEM_ATTR) +#endif /* ARP_QUEUEING */ + +#if LWIP_IGMP +LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP", DMEM_ATTR) +#endif /* LWIP_IGMP */ + +#if (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) /* LWIP_TIMERS */ +LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT", DMEM_ATTR) +#endif /* LWIP_TIMERS */ + +#if LWIP_SNMP +LWIP_MEMPOOL(SNMP_ROOTNODE, MEMP_NUM_SNMP_ROOTNODE, sizeof(struct mib_list_rootnode), "SNMP_ROOTNODE") +LWIP_MEMPOOL(SNMP_NODE, MEMP_NUM_SNMP_NODE, sizeof(struct mib_list_node), "SNMP_NODE") +LWIP_MEMPOOL(SNMP_VARBIND, MEMP_NUM_SNMP_VARBIND, sizeof(struct snmp_varbind), "SNMP_VARBIND") +LWIP_MEMPOOL(SNMP_VALUE, MEMP_NUM_SNMP_VALUE, SNMP_MAX_VALUE_SIZE, "SNMP_VALUE") +#endif /* LWIP_SNMP */ +#if LWIP_DNS && LWIP_SOCKET +LWIP_MEMPOOL(NETDB, MEMP_NUM_NETDB, NETDB_ELEM_SIZE, "NETDB") +#endif /* LWIP_DNS && LWIP_SOCKET */ +#if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC +LWIP_MEMPOOL(LOCALHOSTLIST, MEMP_NUM_LOCALHOSTLIST, LOCALHOSTLIST_ELEM_SIZE, "LOCALHOSTLIST") +#endif /* LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ +#if PPP_SUPPORT && PPPOE_SUPPORT +LWIP_MEMPOOL(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF") +#endif /* PPP_SUPPORT && PPPOE_SUPPORT */ + +/* + * A list of pools of pbuf's used by LWIP. + * + * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + * This allocates enough space for the pbuf struct and a payload. + * (Example: pbuf_payload_size=0 allocates only size for the struct) + */ +LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF, 0, "PBUF_REF/ROM", DMEM_ATTR) + +/* XXX: need to align to 4 byte as memp strcut is 4-byte long. otherwise will crash */ +#define LWIP_MEM_ALIGN4_SIZE(size) (((size) + 4 - 1) & ~(4-1)) + +LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, LWIP_MEM_ALIGN4_SIZE(PBUF_POOL_BUFSIZE), "PBUF_POOL", DMEM_ATTR) + + +/* + * Allow for user-defined pools; this must be explicitly set in lwipopts.h + * since the default is to NOT look for lwippools.h + */ +#if MEMP_USE_CUSTOM_POOLS +#include "lwippools.h" +#endif /* MEMP_USE_CUSTOM_POOLS */ + +/* + * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later + * (#undef is ignored for something that is not defined) + */ +#undef LWIP_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL_START +#undef LWIP_MALLOC_MEMPOOL_END +#undef LWIP_PBUF_MEMPOOL diff --git a/app/include/lwip/netbuf.h b/app/include/lwip/netbuf.h new file mode 100644 index 00000000..b554a579 --- /dev/null +++ b/app/include/lwip/netbuf.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_NETBUF_H__ +#define __LWIP_NETBUF_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This netbuf has dest-addr/port set */ +#define NETBUF_FLAG_DESTADDR 0x01 +/** This netbuf includes a checksum */ +#define NETBUF_FLAG_CHKSUM 0x02 + +struct netbuf { + struct pbuf *p, *ptr; + ip_addr_t addr; + u16_t port; +#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY +#if LWIP_CHECKSUM_ON_COPY + u8_t flags; +#endif /* LWIP_CHECKSUM_ON_COPY */ + u16_t toport_chksum; +#if LWIP_NETBUF_RECVINFO + ip_addr_t toaddr; +#endif /* LWIP_NETBUF_RECVINFO */ +#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ +}; + +/* Network buffer functions: */ +struct netbuf * netbuf_new (void)ICACHE_FLASH_ATTR; +void netbuf_delete (struct netbuf *buf)ICACHE_FLASH_ATTR; +void * netbuf_alloc (struct netbuf *buf, u16_t size)ICACHE_FLASH_ATTR; +void netbuf_free (struct netbuf *buf)ICACHE_FLASH_ATTR; +err_t netbuf_ref (struct netbuf *buf, + const void *dataptr, u16_t size)ICACHE_FLASH_ATTR; +void netbuf_chain (struct netbuf *head, + struct netbuf *tail)ICACHE_FLASH_ATTR; + +err_t netbuf_data (struct netbuf *buf, + void **dataptr, u16_t *len)ICACHE_FLASH_ATTR; +s8_t netbuf_next (struct netbuf *buf)ICACHE_FLASH_ATTR; +void netbuf_first (struct netbuf *buf)ICACHE_FLASH_ATTR; + + +#define netbuf_copy_partial(buf, dataptr, len, offset) \ + pbuf_copy_partial((buf)->p, (dataptr), (len), (offset)) +#define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0) +#define netbuf_take(buf, dataptr, len) pbuf_take((buf)->p, dataptr, len) +#define netbuf_len(buf) ((buf)->p->tot_len) +#define netbuf_fromaddr(buf) (&((buf)->addr)) +#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set((&(buf)->addr), fromaddr) +#define netbuf_fromport(buf) ((buf)->port) +#if LWIP_NETBUF_RECVINFO +#define netbuf_destaddr(buf) (&((buf)->toaddr)) +#define netbuf_set_destaddr(buf, destaddr) ip_addr_set((&(buf)->addr), destaddr) +#define netbuf_destport(buf) (((buf)->flags & NETBUF_FLAG_DESTADDR) ? (buf)->toport_chksum : 0) +#endif /* LWIP_NETBUF_RECVINFO */ +#if LWIP_CHECKSUM_ON_COPY +#define netbuf_set_chksum(buf, chksum) do { (buf)->flags = NETBUF_FLAG_CHKSUM; \ + (buf)->toport_chksum = chksum; } while(0) +#endif /* LWIP_CHECKSUM_ON_COPY */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NETBUF_H__ */ diff --git a/app/include/lwip/netdb.h b/app/include/lwip/netdb.h new file mode 100644 index 00000000..7587e2f2 --- /dev/null +++ b/app/include/lwip/netdb.h @@ -0,0 +1,124 @@ +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef __LWIP_NETDB_H__ +#define __LWIP_NETDB_H__ + +#include "lwip/opt.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include /* for size_t */ + +#include "lwip/inet.h" +#include "lwip/sockets.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* some rarely used options */ +#ifndef LWIP_DNS_API_DECLARE_H_ERRNO +#define LWIP_DNS_API_DECLARE_H_ERRNO 1 +#endif + +#ifndef LWIP_DNS_API_DEFINE_ERRORS +#define LWIP_DNS_API_DEFINE_ERRORS 1 +#endif + +#ifndef LWIP_DNS_API_DECLARE_STRUCTS +#define LWIP_DNS_API_DECLARE_STRUCTS 1 +#endif + +#if LWIP_DNS_API_DEFINE_ERRORS +/** Errors used by the DNS API functions, h_errno can be one of them */ +#define EAI_NONAME 200 +#define EAI_SERVICE 201 +#define EAI_FAIL 202 +#define EAI_MEMORY 203 + +#define HOST_NOT_FOUND 210 +#define NO_DATA 211 +#define NO_RECOVERY 212 +#define TRY_AGAIN 213 +#endif /* LWIP_DNS_API_DEFINE_ERRORS */ + +#if LWIP_DNS_API_DECLARE_STRUCTS +struct hostent { + char *h_name; /* Official name of the host. */ + char **h_aliases; /* A pointer to an array of pointers to alternative host names, + terminated by a null pointer. */ + int h_addrtype; /* Address type. */ + int h_length; /* The length, in bytes, of the address. */ + char **h_addr_list; /* A pointer to an array of pointers to network addresses (in + network byte order) for the host, terminated by a null pointer. */ +#define h_addr h_addr_list[0] /* for backward compatibility */ +}; + +struct addrinfo { + int ai_flags; /* Input flags. */ + int ai_family; /* Address family of socket. */ + int ai_socktype; /* Socket type. */ + int ai_protocol; /* Protocol of socket. */ + socklen_t ai_addrlen; /* Length of socket address. */ + struct sockaddr *ai_addr; /* Socket address of socket. */ + char *ai_canonname; /* Canonical name of service location. */ + struct addrinfo *ai_next; /* Pointer to next in list. */ +}; +#endif /* LWIP_DNS_API_DECLARE_STRUCTS */ + +#if LWIP_DNS_API_DECLARE_H_ERRNO +/* application accessable error code set by the DNS API functions */ +extern int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO*/ + +struct hostent *lwip_gethostbyname(const char *name); +int lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop); +void lwip_freeaddrinfo(struct addrinfo *ai); +int lwip_getaddrinfo(const char *nodename, + const char *servname, + const struct addrinfo *hints, + struct addrinfo **res); + +#if LWIP_COMPAT_SOCKETS +#define gethostbyname(name) lwip_gethostbyname(name) +#define gethostbyname_r(name, ret, buf, buflen, result, h_errnop) \ + lwip_gethostbyname_r(name, ret, buf, buflen, result, h_errnop) +#define freeaddrinfo(addrinfo) lwip_freeaddrinfo(addrinfo) +#define getaddrinfo(nodname, servname, hints, res) \ + lwip_getaddrinfo(nodname, servname, hints, res) +#endif /* LWIP_COMPAT_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DNS && LWIP_SOCKET */ + +#endif /* __LWIP_NETDB_H__ */ diff --git a/app/include/lwip/netif.h b/app/include/lwip/netif.h new file mode 100644 index 00000000..8bf13752 --- /dev/null +++ b/app/include/lwip/netif.h @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_NETIF_H__ +#define __LWIP_NETIF_H__ + +#include "lwip/opt.h" + +#define ENABLE_LOOPBACK (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF) + +#include "lwip/err.h" + +#include "lwip/ip_addr.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#if LWIP_DHCP +struct dhcp; +#endif +#if LWIP_AUTOIP +struct autoip; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses are expected to be in + * the same byte order as in IP_PCB. */ + +/** must be the maximum of all used hardware address lengths + across all types of interfaces in use */ +#define NETIF_MAX_HWADDR_LEN 6U + +/** Whether the network interface is 'up'. This is + * a software flag used to control whether this network + * interface is enabled and processes traffic. + * It is set by the startup code (for static IP configuration) or + * by dhcp/autoip when an address has been assigned. + */ +#define NETIF_FLAG_UP 0x01U +/** If set, the netif has broadcast capability. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_BROADCAST 0x02U +/** If set, the netif is one end of a point-to-point connection. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_POINTTOPOINT 0x04U +/** If set, the interface is configured using DHCP. + * Set by the DHCP code when starting or stopping DHCP. */ +#define NETIF_FLAG_DHCP 0x08U +/** If set, the interface has an active link + * (set by the network interface driver). + * Either set by the netif driver in its init function (if the link + * is up at that time) or at a later point once the link comes up + * (if link detection is supported by the hardware). */ +#define NETIF_FLAG_LINK_UP 0x10U +/** If set, the netif is an ethernet device using ARP. + * Set by the netif driver in its init function. + * Used to check input packet types and use of DHCP. */ +#define NETIF_FLAG_ETHARP 0x20U +/** If set, the netif is an ethernet device. It might not use + * ARP or TCP/IP if it is used for PPPoE only. + */ +#define NETIF_FLAG_ETHERNET 0x40U +/** If set, the netif has IGMP capability. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_IGMP 0x80U + +/** Function prototype for netif init functions. Set up flags and output/linkoutput + * callback functions in this function. + * + * @param netif The netif to initialize + */ +typedef err_t (*netif_init_fn)(struct netif *netif); +/** Function prototype for netif->input functions. This function is saved as 'input' + * callback function in the netif struct. Call it when a packet has been received. + * + * @param p The received packet, copied into a pbuf + * @param inp The netif which received the packet + */ +typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp); +/** Function prototype for netif->output functions. Called by lwIP when a packet + * shall be sent. For ethernet netif, set this to 'etharp_output' and set + * 'linkoutput'. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (p->payload points to IP header) + * @param ipaddr The IP address to which the packet shall be sent + */ +typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p, + ip_addr_t *ipaddr); +/** Function prototype for netif->linkoutput functions. Only used for ethernet + * netifs. This function is called by ARP when a packet shall be sent. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (raw ethernet packet) + */ +typedef err_t (*netif_linkoutput_fn)(struct netif *netif, struct pbuf *p); +/** Function prototype for netif status- or link-callback functions. */ +typedef void (*netif_status_callback_fn)(struct netif *netif); +/** Function prototype for netif igmp_mac_filter functions */ +typedef err_t (*netif_igmp_mac_filter_fn)(struct netif *netif, + ip_addr_t *group, u8_t action); + +/** Generic data structure used for all lwIP network interfaces. + * The following fields should be filled in by the initialization + * function for the device driver: hwaddr_len, hwaddr[], mtu, flags */ +struct netif { + /** pointer to next in linked list */ + struct netif *next; + + /** IP address configuration in network byte order */ + ip_addr_t ip_addr; + ip_addr_t netmask; + ip_addr_t gw; + + /** This function is called by the network device driver + * to pass a packet up the TCP/IP stack. IPݰ*/ + netif_input_fn input; + /** This function is called by the IP module when it wants + * to send a packet on the interface. This function typically + * first resolves the hardware address, then sends the packet. IPݰ*/ + netif_output_fn output; + /** This function is called by the ARP module when it wants + * to send a packet on the interface. This function outputs + * the pbuf as-is on the link medium. ײݰ*/ + netif_linkoutput_fn linkoutput; +#if LWIP_NETIF_STATUS_CALLBACK + /** This function is called when the netif state is set to up or down + */ + netif_status_callback_fn status_callback; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + /** This function is called when the netif link is set to up or down + */ + netif_status_callback_fn link_callback; +#endif /* LWIP_NETIF_LINK_CALLBACK */ + /** This field can be set by the device driver and could point + * to state information for the device. ֶΣָײ豸Ϣ*/ + void *state; +#if LWIP_DHCP + /** the DHCP client state information for this netif */ + struct dhcp *dhcp; +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /** the AutoIP client state information for this netif */ + struct autoip *autoip; +#endif +#if LWIP_NETIF_HOSTNAME + /* the hostname for this netif, NULL is a valid value */ + char* hostname; +#endif /* LWIP_NETIF_HOSTNAME */ + /** maximum transfer unit (in bytes) ýӿݰȣ1500*/ + u16_t mtu; + /** number of bytes used in hwaddrýӿַ */ + u8_t hwaddr_len; + /** link level hardware address of this interface ýӿַ*/ + u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; + /** flags (see NETIF_FLAG_ above) ýӿ״ֶ̬*/ + u8_t flags; + /** descriptive abbreviation ýӿڵ*/ + char name[2]; + /** number of this interface ýӿڵı*/ + u8_t num; +#if LWIP_SNMP + /** link type (from "snmp_ifType" enum from snmp.h) */ + u8_t link_type; + /** (estimate) link speed */ + u32_t link_speed; + /** timestamp at last change made (up/down) */ + u32_t ts; + /** counters */ + u32_t ifinoctets; + u32_t ifinucastpkts; + u32_t ifinnucastpkts; + u32_t ifindiscards; + u32_t ifoutoctets; + u32_t ifoutucastpkts; + u32_t ifoutnucastpkts; + u32_t ifoutdiscards; +#endif /* LWIP_SNMP */ +#if LWIP_IGMP + /** This function could be called to add or delete a entry in the multicast + filter table of the ethernet MAC.*/ + netif_igmp_mac_filter_fn igmp_mac_filter; +#endif /* LWIP_IGMP */ +#if LWIP_NETIF_HWADDRHINT + u8_t *addr_hint; +#endif /* LWIP_NETIF_HWADDRHINT */ +#if ENABLE_LOOPBACK + /* List of packets to be queued for ourselves. ָ͸Լݰpbuf*/ + struct pbuf *loop_first;//һ + struct pbuf *loop_last;//һ +#if LWIP_LOOPBACK_MAX_PBUFS + u16_t loop_cnt_current; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ +#endif /* ENABLE_LOOPBACK */ +}; + +#if LWIP_SNMP +#define NETIF_INIT_SNMP(netif, type, speed) \ + /* use "snmp_ifType" enum from snmp.h for "type", snmp_ifType_ethernet_csmacd by example */ \ + (netif)->link_type = (type); \ + /* your link speed here (units: bits per second) */ \ + (netif)->link_speed = (speed); \ + (netif)->ts = 0; \ + (netif)->ifinoctets = 0; \ + (netif)->ifinucastpkts = 0; \ + (netif)->ifinnucastpkts = 0; \ + (netif)->ifindiscards = 0; \ + (netif)->ifoutoctets = 0; \ + (netif)->ifoutucastpkts = 0; \ + (netif)->ifoutnucastpkts = 0; \ + (netif)->ifoutdiscards = 0 +#else /* LWIP_SNMP */ +#define NETIF_INIT_SNMP(netif, type, speed) +#endif /* LWIP_SNMP */ + + +/** The list of network interfaces. */ +extern struct netif *netif_list; +/** The default network interface. */ +extern struct netif *netif_default; + +void netif_init(void)ICACHE_FLASH_ATTR; + +struct netif *netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input)ICACHE_FLASH_ATTR; + +void +netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw)ICACHE_FLASH_ATTR; +void netif_remove(struct netif * netif)ICACHE_FLASH_ATTR; + +/* Returns a network interface given its name. The name is of the form + "et0", where the first two letters are the "name" field in the + netif structure, and the digit is in the num field in the same + structure. */ +struct netif *netif_find(char *name)ICACHE_FLASH_ATTR; + +void netif_set_default(struct netif *netif)ICACHE_FLASH_ATTR; + +void netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr)ICACHE_FLASH_ATTR; +void netif_set_netmask(struct netif *netif, ip_addr_t *netmask)ICACHE_FLASH_ATTR; +void netif_set_gw(struct netif *netif, ip_addr_t *gw)ICACHE_FLASH_ATTR; + +void netif_set_up(struct netif *netif)ICACHE_FLASH_ATTR; +void netif_set_down(struct netif *netif)ICACHE_FLASH_ATTR; +/** Ask if an interface is up */ +#define netif_is_up(netif) (((netif)->flags & NETIF_FLAG_UP) ? (u8_t)1 : (u8_t)0) + +#if LWIP_NETIF_STATUS_CALLBACK +void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback)ICACHE_FLASH_ATTR; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +void netif_set_link_up(struct netif *netif)ICACHE_FLASH_ATTR; +void netif_set_link_down(struct netif *netif)ICACHE_FLASH_ATTR; +/** Ask if a link is up */ +#define netif_is_link_up(netif) (((netif)->flags & NETIF_FLAG_LINK_UP) ? (u8_t)1 : (u8_t)0) + +#if LWIP_NETIF_LINK_CALLBACK +void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback)ICACHE_FLASH_ATTR; +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if LWIP_NETIF_HOSTNAME +#define netif_set_hostname(netif, name) do { if((netif) != NULL) { (netif)->hostname = name; }}while(0) +#define netif_get_hostname(netif) (((netif) != NULL) ? ((netif)->hostname) : NULL) +#endif /* LWIP_NETIF_HOSTNAME */ + +#if LWIP_IGMP +#define netif_set_igmp_mac_filter(netif, function) do { if((netif) != NULL) { (netif)->igmp_mac_filter = function; }}while(0) +#define netif_get_igmp_mac_filter(netif) (((netif) != NULL) ? ((netif)->igmp_mac_filter) : NULL) +#endif /* LWIP_IGMP */ + +#if ENABLE_LOOPBACK +err_t netif_loop_output(struct netif *netif, struct pbuf *p, ip_addr_t *dest_ip)ICACHE_FLASH_ATTR; +void netif_poll(struct netif *netif)ICACHE_FLASH_ATTR; +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +void netif_poll_all(void)ICACHE_FLASH_ATTR; +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NETIF_H__ */ diff --git a/app/include/lwip/netifapi.h b/app/include/lwip/netifapi.h new file mode 100644 index 00000000..33318efa --- /dev/null +++ b/app/include/lwip/netifapi.h @@ -0,0 +1,108 @@ +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#ifndef __LWIP_NETIFAPI_H__ +#define __LWIP_NETIFAPI_H__ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/netif.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*netifapi_void_fn)(struct netif *netif); +typedef err_t (*netifapi_errt_fn)(struct netif *netif); + +struct netifapi_msg_msg { +#if !LWIP_TCPIP_CORE_LOCKING + sys_sem_t sem; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + err_t err; + struct netif *netif; + union { + struct { + ip_addr_t *ipaddr; + ip_addr_t *netmask; + ip_addr_t *gw; + void *state; + netif_init_fn init; + netif_input_fn input; + } add; + struct { + netifapi_void_fn voidfunc; + netifapi_errt_fn errtfunc; + } common; + } msg; +}; + +struct netifapi_msg { + void (* function)(struct netifapi_msg_msg *msg); + struct netifapi_msg_msg msg; +}; + + +/* API for application */ +err_t netifapi_netif_add ( struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw, + void *state, + netif_init_fn init, + netif_input_fn input); + +err_t netifapi_netif_set_addr ( struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw ); + +err_t netifapi_netif_common ( struct netif *netif, + netifapi_void_fn voidfunc, + netifapi_errt_fn errtfunc); + +#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL) +#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL) +#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL) +#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL) +#define netifapi_dhcp_start(n) netifapi_netif_common(n, NULL, dhcp_start) +#define netifapi_dhcp_stop(n) netifapi_netif_common(n, dhcp_stop, NULL) +#define netifapi_autoip_start(n) netifapi_netif_common(n, NULL, autoip_start) +#define netifapi_autoip_stop(n) netifapi_netif_common(n, NULL, autoip_stop) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETIF_API */ + +#endif /* __LWIP_NETIFAPI_H__ */ diff --git a/app/include/lwip/opt.h b/app/include/lwip/opt.h new file mode 100644 index 00000000..0d2b3fcc --- /dev/null +++ b/app/include/lwip/opt.h @@ -0,0 +1,2043 @@ +/** + * @file + * + * lwIP Options Configuration + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_OPT_H__ +#define __LWIP_OPT_H__ + +/* + * Include user defined options first. Anything not defined in these files + * will be set to standard values. Override anything you dont like! + */ +#include "lwipopts.h" +#include "lwip/debug.h" + +/* + ----------------------------------------------- + ---------- Platform specific locking ---------- + ----------------------------------------------- +*/ + +/** + * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain + * critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#ifndef SYS_LIGHTWEIGHT_PROT +#define SYS_LIGHTWEIGHT_PROT 0 +#endif + +/** + * NO_SYS==1: Provides VERY minimal functionality. Otherwise, + * use lwIP facilities. + */ +#ifndef NO_SYS +#define NO_SYS 1 +#endif + +/** + * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1 + * Mainly for compatibility to old versions. + */ +#ifndef NO_SYS_NO_TIMERS +#define NO_SYS_NO_TIMERS 1 +#endif + +/** + * MEMCPY: override this if you have a faster implementation at hand than the + * one included in your C library + */ +#ifndef MEMCPY +#define MEMCPY(dst,src,len) memcpy(dst,src,len) +#endif + +/** + * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a + * call to memcpy() if the length is known at compile time and is small. + */ +#ifndef SMEMCPY +#define SMEMCPY(dst,src,len) memcpy(dst,src,len) +#endif + +/* + ------------------------------------ + ---------- Memory options ---------- + ------------------------------------ +*/ +/** + * 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. + */ +#ifndef MEM_LIBC_MALLOC +#define MEM_LIBC_MALLOC 0 +#endif + +/** +* 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! +*/ +#ifndef MEMP_MEM_MALLOC +#define MEMP_MEM_MALLOC 0 +#endif + +/** + * MEM_ALIGNMENT: should be set to the alignment of the CPU + * 4 byte alignment -> #define MEM_ALIGNMENT 4 + * 2 byte alignment -> #define MEM_ALIGNMENT 2 + */ +#ifndef MEM_ALIGNMENT +#define MEM_ALIGNMENT 1 +#endif + +/** + * 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. + */ +#ifndef MEM_SIZE +#define MEM_SIZE 1600 +#endif + +/** + * MEMP_SEPARATE_POOLS: if defined to 1, each pool is placed in its own array. + * This can be used to individually change the location of each pool. + * Default is one big array for all pools + */ +#ifndef MEMP_SEPARATE_POOLS +#define MEMP_SEPARATE_POOLS 0 +#endif + +/** + * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable + * amount of bytes before and after each memp element in every pool and fills + * it with a prominent default value. + * MEMP_OVERFLOW_CHECK == 0 no checking + * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed + * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time + * memp_malloc() or memp_free() is called (useful but slow!) + */ +#ifndef MEMP_OVERFLOW_CHECK +#define MEMP_OVERFLOW_CHECK 0 +#endif + +/** + * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make + * sure that there are no cycles in the linked lists. + */ +#ifndef MEMP_SANITY_CHECK +#define MEMP_SANITY_CHECK 0 +#endif + +/** + * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set + * of memory pools of various sizes. When mem_malloc is called, an element of + * the smallest pool that can provide the length needed is returned. + * To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled. + */ +#ifndef MEM_USE_POOLS +#define MEM_USE_POOLS 0 +#endif + +/** + * MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next + * bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more + * reliable. */ +#ifndef MEM_USE_POOLS_TRY_BIGGER_POOL +#define MEM_USE_POOLS_TRY_BIGGER_POOL 0 +#endif + +/** + * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h + * that defines additional pools beyond the "standard" ones required + * by lwIP. If you set this to 1, you must have lwippools.h in your + * inlude path somewhere. + */ +#ifndef MEMP_USE_CUSTOM_POOLS +#define MEMP_USE_CUSTOM_POOLS 0 +#endif + +/** + * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from + * interrupt context (or another context that doesn't allow waiting for a + * semaphore). + * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT, + * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs + * with each loop so that mem_free can run. + * + * ATTENTION: As you can see from the above description, this leads to dis-/ + * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc + * can need longer. + * + * If you don't want that, at least for NO_SYS=0, you can still use the following + * functions to enqueue a deallocation call which then runs in the tcpip_thread + * context: + * - pbuf_free_callback(p); + * - mem_free_callback(m); + */ +#ifndef LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0 +#endif + +/* + ------------------------------------------------ + ---------- Internal Memory Pool Sizes ---------- + ------------------------------------------------ +*/ +/** + * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF). + * If the application sends a lot of data out of ROM (or other static memory), + * this should be set high. + */ +#ifndef MEMP_NUM_PBUF +#define MEMP_NUM_PBUF 16 +#endif + +/** + * MEMP_NUM_RAW_PCB: Number of raw connection PCBs + * (requires the LWIP_RAW option) + */ +#ifndef MEMP_NUM_RAW_PCB +#define MEMP_NUM_RAW_PCB 4 +#endif + +/** + * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + * per active UDP "connection". + * (requires the LWIP_UDP option) + */ +#ifndef MEMP_NUM_UDP_PCB +#define MEMP_NUM_UDP_PCB 4 +#endif + +/** + * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_PCB +#define MEMP_NUM_TCP_PCB 5 +#endif + +/** + * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_PCB_LISTEN +#define MEMP_NUM_TCP_PCB_LISTEN 8 +#endif + +/** + * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_SEG +#define MEMP_NUM_TCP_SEG 16 +#endif + +/** + * MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for + * reassembly (whole packets, not fragments!) + */ +#ifndef MEMP_NUM_REASSDATA +#define MEMP_NUM_REASSDATA 5 +#endif + +/** + * MEMP_NUM_FRAG_PBUF: the number of IP fragments simultaneously sent + * (fragments, not whole packets!). + * This is only used with IP_FRAG_USES_STATIC_BUF==0 and + * LWIP_NETIF_TX_SINGLE_PBUF==0 and only has to be > 1 with DMA-enabled MACs + * where the packet is not yet sent when netif->output returns. + */ +#ifndef MEMP_NUM_FRAG_PBUF +#define MEMP_NUM_FRAG_PBUF 15 +#endif + +/** + * MEMP_NUM_ARP_QUEUE: the number of simulateously queued outgoing + * packets (pbufs) that are waiting for an ARP request (to resolve + * their destination address) to finish. + * (requires the ARP_QUEUEING option) + */ +#ifndef MEMP_NUM_ARP_QUEUE +#define MEMP_NUM_ARP_QUEUE 30 +#endif + +/** + * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces + * can be members et the same time (one per netif - allsystems group -, plus one + * per netif membership). + * (requires the LWIP_IGMP option) + */ +#ifndef MEMP_NUM_IGMP_GROUP +#define MEMP_NUM_IGMP_GROUP 8 +#endif + +/** + * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts. + * (requires NO_SYS==0) + */ +#ifndef MEMP_NUM_SYS_TIMEOUT +#define MEMP_NUM_SYS_TIMEOUT 3 +#endif + +/** + * MEMP_NUM_NETBUF: the number of struct netbufs. + * (only needed if you use the sequential API, like api_lib.c) + */ +#ifndef MEMP_NUM_NETBUF +#define MEMP_NUM_NETBUF 2 +#endif + +/** + * MEMP_NUM_NETCONN: the number of struct netconns. + * (only needed if you use the sequential API, like api_lib.c) + */ +#ifndef MEMP_NUM_NETCONN +#define MEMP_NUM_NETCONN 4 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used + * for callback/timeout API communication. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_API +#define MEMP_NUM_TCPIP_MSG_API 8 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used + * for incoming packets. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_INPKT +#define MEMP_NUM_TCPIP_MSG_INPKT 8 +#endif + +/** + * MEMP_NUM_SNMP_NODE: the number of leafs in the SNMP tree. + */ +#ifndef MEMP_NUM_SNMP_NODE +#define MEMP_NUM_SNMP_NODE 50 +#endif + +/** + * MEMP_NUM_SNMP_ROOTNODE: the number of branches in the SNMP tree. + * Every branch has one leaf (MEMP_NUM_SNMP_NODE) at least! + */ +#ifndef MEMP_NUM_SNMP_ROOTNODE +#define MEMP_NUM_SNMP_ROOTNODE 30 +#endif + +/** + * MEMP_NUM_SNMP_VARBIND: the number of concurrent requests (does not have to + * be changed normally) - 2 of these are used per request (1 for input, + * 1 for output) + */ +#ifndef MEMP_NUM_SNMP_VARBIND +#define MEMP_NUM_SNMP_VARBIND 2 +#endif + +/** + * MEMP_NUM_SNMP_VALUE: the number of OID or values concurrently used + * (does not have to be changed normally) - 3 of these are used per request + * (1 for the value read and 2 for OIDs - input and output) + */ +#ifndef MEMP_NUM_SNMP_VALUE +#define MEMP_NUM_SNMP_VALUE 3 +#endif + +/** + * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls + * (before freeing the corresponding memory using lwip_freeaddrinfo()). + */ +#ifndef MEMP_NUM_NETDB +#define MEMP_NUM_NETDB 1 +#endif + +/** + * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list + * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1. + */ +#ifndef MEMP_NUM_LOCALHOSTLIST +#define MEMP_NUM_LOCALHOSTLIST 1 +#endif + +/** + * MEMP_NUM_PPPOE_INTERFACES: the number of concurrently active PPPoE + * interfaces (only used with PPPOE_SUPPORT==1) + */ +#ifndef MEMP_NUM_PPPOE_INTERFACES +#define MEMP_NUM_PPPOE_INTERFACES 1 +#endif + +/** + * PBUF_POOL_SIZE: the number of buffers in the pbuf pool. + */ +#ifndef PBUF_POOL_SIZE +#define PBUF_POOL_SIZE 16 +#endif + +/* + --------------------------------- + ---------- ARP options ---------- + --------------------------------- +*/ +/** + * LWIP_ARP==1: Enable ARP functionality. + */ +#ifndef LWIP_ARP +#define LWIP_ARP 1 +#endif + +/** + * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached. + */ +#ifndef ARP_TABLE_SIZE +#define ARP_TABLE_SIZE 10 +#endif + +/** + * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address + * resolution. By default, only the most recent packet is queued per IP address. + * This is sufficient for most protocols and mainly reduces TCP connection + * startup time. Set this to 1 if you know your application sends more than one + * packet in a row to an IP address that is not in the ARP cache. + */ +#ifndef ARP_QUEUEING +#define ARP_QUEUEING 0 +#endif + +/** + * ETHARP_TRUST_IP_MAC==1: Incoming IP packets cause the ARP table to be + * updated with the source MAC and IP addresses supplied in the packet. + * You may want to disable this if you do not trust LAN peers to have the + * correct addresses, or as a limited approach to attempt to handle + * spoofing. If disabled, lwIP will need to make a new ARP request if + * the peer is not already in the ARP table, adding a little latency. + * The peer *is* in the ARP table if it requested our address before. + * Also notice that this slows down input processing of every IP packet! + */ +#ifndef ETHARP_TRUST_IP_MAC +#define ETHARP_TRUST_IP_MAC 0 +#endif + +/** + * ETHARP_SUPPORT_VLAN==1: support receiving ethernet packets with VLAN header. + * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check. + * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted. + * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted. + */ +#ifndef ETHARP_SUPPORT_VLAN +#define ETHARP_SUPPORT_VLAN 0 +#endif + +/** LWIP_ETHERNET==1: enable ethernet support for PPPoE even though ARP + * might be disabled + */ +#ifndef LWIP_ETHERNET +#define LWIP_ETHERNET (LWIP_ARP || PPPOE_SUPPORT) +#endif + +/** ETH_PAD_SIZE: number of bytes added before the ethernet header to ensure + * alignment of payload after that header. Since the header is 14 bytes long, + * without this padding e.g. addresses in the IP header will not be aligned + * on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms. + */ +#ifndef ETH_PAD_SIZE +#define ETH_PAD_SIZE 0 +#endif + +/** ETHARP_SUPPORT_STATIC_ENTRIES==1: enable code to support static ARP table + * entries (using etharp_add_static_entry/etharp_remove_static_entry). + */ +#ifndef ETHARP_SUPPORT_STATIC_ENTRIES +#define ETHARP_SUPPORT_STATIC_ENTRIES 0 +#endif + + +/* + -------------------------------- + ---------- IP options ---------- + -------------------------------- +*/ +/** + * IP_FORWARD==1: Enables the ability to forward IP packets across network + * interfaces. If you are going to run lwIP on a device with only one network + * interface, define this to 0. + */ +#ifndef IP_FORWARD +#define IP_FORWARD 0 +#endif + +/** + * IP_OPTIONS_ALLOWED: Defines the behavior for IP options. + * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped. + * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed). + */ +#ifndef IP_OPTIONS_ALLOWED +#define IP_OPTIONS_ALLOWED 1 +#endif + +/** + * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that + * this option does not affect outgoing packet sizes, which can be controlled + * via IP_FRAG. + */ +#ifndef IP_REASSEMBLY +#define IP_REASSEMBLY 0 +#endif + +/** + * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note + * that this option does not affect incoming packet sizes, which can be + * controlled via IP_REASSEMBLY. + */ +#ifndef IP_FRAG +#define IP_FRAG 1 +#endif + +/** + * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) + * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived + * in this time, the whole packet is discarded. + */ +#ifndef IP_REASS_MAXAGE +#define IP_REASS_MAXAGE 3 +#endif + +/** + * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled. + * Since the received pbufs are enqueued, be sure to configure + * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive + * packets even if the maximum amount of fragments is enqueued for reassembly! + */ +#ifndef IP_REASS_MAX_PBUFS +#define IP_REASS_MAX_PBUFS 10 +#endif + +/** + * IP_FRAG_USES_STATIC_BUF==1: Use a static MTU-sized buffer for IP + * fragmentation. Otherwise pbufs are allocated and reference the original + * packet data to be fragmented (or with LWIP_NETIF_TX_SINGLE_PBUF==1, + * new PBUF_RAM pbufs are used for fragments). + * ATTENTION: IP_FRAG_USES_STATIC_BUF==1 may not be used for DMA-enabled MACs! + */ +#ifndef IP_FRAG_USES_STATIC_BUF +#define IP_FRAG_USES_STATIC_BUF 0 +#endif + +/** + * IP_FRAG_MAX_MTU: Assumed max MTU on any interface for IP frag buffer + * (requires IP_FRAG_USES_STATIC_BUF==1) + */ +#if IP_FRAG_USES_STATIC_BUF && !defined(IP_FRAG_MAX_MTU) +#define IP_FRAG_MAX_MTU 1500 +#endif + +/** + * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers. + */ +#ifndef IP_DEFAULT_TTL +#define IP_DEFAULT_TTL 255 +#endif + +/** + * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast + * filter per pcb on udp and raw send operations. To enable broadcast filter + * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1. + */ +#ifndef IP_SOF_BROADCAST +#define IP_SOF_BROADCAST 0 +#endif + +/** + * IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast + * filter on recv operations. + */ +#ifndef IP_SOF_BROADCAST_RECV +#define IP_SOF_BROADCAST_RECV 0 +#endif + +/* + ---------------------------------- + ---------- ICMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_ICMP==1: Enable ICMP module inside the IP stack. + * Be careful, disable that make your product non-compliant to RFC1122 + */ +#ifndef LWIP_ICMP +#define LWIP_ICMP 1 +#endif + +/** + * ICMP_TTL: Default value for Time-To-Live used by ICMP packets. + */ +#ifndef ICMP_TTL +#define ICMP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only) + */ +#ifndef LWIP_BROADCAST_PING +#define LWIP_BROADCAST_PING 0 +#endif + +/** + * LWIP_MULTICAST_PING==1: respond to multicast pings (default is unicast only) + */ +#ifndef LWIP_MULTICAST_PING +#define LWIP_MULTICAST_PING 0 +#endif + +/* + --------------------------------- + ---------- RAW options ---------- + --------------------------------- +*/ +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#ifndef LWIP_RAW +#define LWIP_RAW 1 +#endif + +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#ifndef RAW_TTL +#define RAW_TTL (IP_DEFAULT_TTL) +#endif + +/* + ---------------------------------- + ---------- DHCP options ---------- + ---------------------------------- +*/ +/** + * LWIP_DHCP==1: Enable DHCP module. + */ +#ifndef LWIP_DHCP +#define LWIP_DHCP 0 +#endif + +/** + * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address. + */ +#ifndef DHCP_DOES_ARP_CHECK +#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP)) +#endif + +/* + ------------------------------------ + ---------- AUTOIP options ---------- + ------------------------------------ +*/ +/** + * LWIP_AUTOIP==1: Enable AUTOIP module. + */ +#ifndef LWIP_AUTOIP +#define LWIP_AUTOIP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on + * the same interface at the same time. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP +#define LWIP_DHCP_AUTOIP_COOP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes + * that should be sent before falling back on AUTOIP. This can be set + * as low as 1 to get an AutoIP address very quickly, but you should + * be prepared to handle a changing IP address when DHCP overrides + * AutoIP. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP_TRIES +#define LWIP_DHCP_AUTOIP_COOP_TRIES 9 +#endif + +/* + ---------------------------------- + ---------- SNMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_SNMP==1: Turn on SNMP module. UDP must be available for SNMP + * transport. + */ +#ifndef LWIP_SNMP +#define LWIP_SNMP 0 +#endif + +/** + * SNMP_CONCURRENT_REQUESTS: Number of concurrent requests the module will + * allow. At least one request buffer is required. + * Does not have to be changed unless external MIBs answer request asynchronously + */ +#ifndef SNMP_CONCURRENT_REQUESTS +#define SNMP_CONCURRENT_REQUESTS 1 +#endif + +/** + * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap + * destination is required + */ +#ifndef SNMP_TRAP_DESTINATIONS +#define SNMP_TRAP_DESTINATIONS 1 +#endif + +/** + * SNMP_PRIVATE_MIB: + * When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. + */ +#ifndef SNMP_PRIVATE_MIB +#define SNMP_PRIVATE_MIB 0 +#endif + +/** + * Only allow SNMP write actions that are 'safe' (e.g. disabeling netifs is not + * a safe action and disabled when SNMP_SAFE_REQUESTS = 1). + * Unsafe requests are disabled by default! + */ +#ifndef SNMP_SAFE_REQUESTS +#define SNMP_SAFE_REQUESTS 1 +#endif + +/** + * The maximum length of strings used. This affects the size of + * MEMP_SNMP_VALUE elements. + */ +#ifndef SNMP_MAX_OCTET_STRING_LEN +#define SNMP_MAX_OCTET_STRING_LEN 127 +#endif + +/** + * The maximum depth of the SNMP tree. + * With private MIBs enabled, this depends on your MIB! + * This affects the size of MEMP_SNMP_VALUE elements. + */ +#ifndef SNMP_MAX_TREE_DEPTH +#define SNMP_MAX_TREE_DEPTH 15 +#endif + +/** + * The size of the MEMP_SNMP_VALUE elements, normally calculated from + * SNMP_MAX_OCTET_STRING_LEN and SNMP_MAX_TREE_DEPTH. + */ +#ifndef SNMP_MAX_VALUE_SIZE +#define SNMP_MAX_VALUE_SIZE LWIP_MAX((SNMP_MAX_OCTET_STRING_LEN)+1, sizeof(s32_t)*(SNMP_MAX_TREE_DEPTH)) +#endif + +/* + ---------------------------------- + ---------- IGMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_IGMP==1: Turn on IGMP module. + */ +#ifndef LWIP_IGMP +#define LWIP_IGMP 0 +#endif + +/* + ---------------------------------- + ---------- DNS options ----------- + ---------------------------------- +*/ +/** + * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS + * transport. + */ +#ifndef LWIP_DNS +#define LWIP_DNS 0 +#endif + +/** DNS maximum number of entries to maintain locally. */ +#ifndef DNS_TABLE_SIZE +#define DNS_TABLE_SIZE 4 +#endif + +/** DNS maximum host name length supported in the name table. */ +#ifndef DNS_MAX_NAME_LENGTH +#define DNS_MAX_NAME_LENGTH 256 +#endif + +/** The maximum of DNS servers */ +#ifndef DNS_MAX_SERVERS +#define DNS_MAX_SERVERS 2 +#endif + +/** DNS do a name checking between the query and the response. */ +#ifndef DNS_DOES_NAME_CHECK +#define DNS_DOES_NAME_CHECK 1 +#endif + +/** DNS message max. size. Default value is RFC compliant. */ +#ifndef DNS_MSG_SIZE +#define DNS_MSG_SIZE 512 +#endif + +/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled, + * you have to define + * #define DNS_LOCAL_HOSTLIST_INIT {{"host1", 0x123}, {"host2", 0x234}} + * (an array of structs name/address, where address is an u32_t in network + * byte order). + * + * Instead, you can also use an external function: + * #define DNS_LOOKUP_LOCAL_EXTERN(x) extern u32_t my_lookup_function(const char *name) + * that returns the IP address or INADDR_NONE if not found. + */ +#ifndef DNS_LOCAL_HOSTLIST +#define DNS_LOCAL_HOSTLIST 0 +#endif /* DNS_LOCAL_HOSTLIST */ + +/** If this is turned on, the local host-list can be dynamically changed + * at runtime. */ +#ifndef DNS_LOCAL_HOSTLIST_IS_DYNAMIC +#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0 +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/* + --------------------------------- + ---------- UDP options ---------- + --------------------------------- +*/ +/** + * LWIP_UDP==1: Turn on UDP. + */ +#ifndef LWIP_UDP +#define LWIP_UDP 1 +#endif + +/** + * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP) + */ +#ifndef LWIP_UDPLITE +#define LWIP_UDPLITE 0 +#endif + +/** + * UDP_TTL: Default Time-To-Live value. + */ +#ifndef UDP_TTL +#define UDP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_NETBUF_RECVINFO==1: append destination addr and port to every netbuf. + */ +#ifndef LWIP_NETBUF_RECVINFO +#define LWIP_NETBUF_RECVINFO 0 +#endif + +/* + --------------------------------- + ---------- TCP options ---------- + --------------------------------- +*/ +/** + * LWIP_TCP==1: Turn on TCP. + */ +#ifndef LWIP_TCP +#define LWIP_TCP 1 +#endif + +/** + * TCP_TTL: Default Time-To-Live value. + */ +#ifndef TCP_TTL +#define TCP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * TCP_WND: The size of a TCP window. This must be at least + * (2 * TCP_MSS) for things to work well + */ +#ifndef TCP_WND +#define TCP_WND (4 * TCP_MSS) +#endif + +/** + * TCP_MAXRTX: Maximum number of retransmissions of data segments. + */ +#ifndef TCP_MAXRTX +#define TCP_MAXRTX 12 +#endif + +/** + * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments. + */ +#ifndef TCP_SYNMAXRTX +#define TCP_SYNMAXRTX 6 +#endif + +/** + * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order. + * Define to 0 if your device is low on memory. + */ +#ifndef TCP_QUEUE_OOSEQ +#define TCP_QUEUE_OOSEQ (LWIP_TCP) +#endif + +/** + * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default, + * you might want to increase this.) + * For the receive side, this MSS is advertised to the remote side + * when opening a connection. For the transmit size, this MSS sets + * an upper limit on the MSS advertised by the remote host. + */ +#ifndef TCP_MSS +#define TCP_MSS 536 +#endif + +/** + * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really + * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which + * reflects the available reassembly buffer size at the remote host) and the + * largest size permitted by the IP layer" (RFC 1122) + * Setting this to 1 enables code that checks TCP_MSS against the MTU of the + * netif used for a connection and limits the MSS if it would be too big otherwise. + */ +#ifndef TCP_CALCULATE_EFF_SEND_MSS +#define TCP_CALCULATE_EFF_SEND_MSS 1 +#endif + + +/** + * TCP_SND_BUF: TCP sender buffer space (bytes). + */ +#ifndef TCP_SND_BUF +#define TCP_SND_BUF 256 +#endif + +/** + * 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. + */ +#ifndef TCP_SND_QUEUELEN +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS)) +#endif + +/** + * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than + * TCP_SND_BUF. It is the amount of space which must be available in the + * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT). + */ +#ifndef TCP_SNDLOWAT +#define TCP_SNDLOWAT ((TCP_SND_BUF)/2) +#endif + +/** + * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be grater + * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below + * this number, select returns writable (combined with TCP_SNDLOWAT). + */ +#ifndef TCP_SNDQUEUELOWAT +#define TCP_SNDQUEUELOWAT ((TCP_SND_QUEUELEN)/2) +#endif + +/** + * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb. + */ +#ifndef TCP_LISTEN_BACKLOG +#define TCP_LISTEN_BACKLOG 0 +#endif + +/** + * The maximum allowed backlog for TCP listen netconns. + * This backlog is used unless another is explicitly specified. + * 0xff is the maximum (u8_t). + */ +#ifndef TCP_DEFAULT_LISTEN_BACKLOG +#define TCP_DEFAULT_LISTEN_BACKLOG 0xff +#endif + +/** + * TCP_OVERSIZE: The maximum number of bytes that tcp_write may + * allocate ahead of time in an attempt to create shorter pbuf chains + * for transmission. The meaningful range is 0 to TCP_MSS. Some + * suggested values are: + * + * 0: Disable oversized allocation. Each tcp_write() allocates a new + pbuf (old behaviour). + * 1: Allocate size-aligned pbufs with minimal excess. Use this if your + * scatter-gather DMA requires aligned fragments. + * 128: Limit the pbuf/memory overhead to 20%. + * TCP_MSS: Try to create unfragmented TCP packets. + * TCP_MSS/4: Try to create 4 fragments or less per TCP packet. + */ +#ifndef TCP_OVERSIZE +#define TCP_OVERSIZE TCP_MSS +#endif + +/** + * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option. + */ +#ifndef LWIP_TCP_TIMESTAMPS +#define LWIP_TCP_TIMESTAMPS 0 +#endif + +/** + * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an + * explicit window update + */ +#ifndef TCP_WND_UPDATE_THRESHOLD +#define TCP_WND_UPDATE_THRESHOLD (TCP_WND / 4) +#endif + +/** + * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1. + * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all + * events (accept, sent, etc) that happen in the system. + * LWIP_CALLBACK_API==1: The PCB callback function is called directly + * for the event. + */ +//#ifndef LWIP_EVENT_API +//#define LWIP_EVENT_API 0 +//#define LWIP_CALLBACK_API 1 +//#else +//#define LWIP_EVENT_API 1 +//#define LWIP_CALLBACK_API 0 +//#endif + + +/* + ---------------------------------- + ---------- Pbuf options ---------- + ---------------------------------- +*/ +/** + * PBUF_LINK_HLEN: the number of bytes that should be allocated for a + * link level header. The default is 14, the standard value for + * Ethernet. + */ +#ifndef PBUF_LINK_HLEN +#define PBUF_LINK_HLEN (14 + ETH_PAD_SIZE) +#endif + +/** + * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is + * designed to accomodate single full size TCP frame in one pbuf, including + * TCP_MSS, IP header, and link header. + */ +#ifndef PBUF_POOL_BUFSIZE +#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN) +#endif + +/* + ------------------------------------------------ + ---------- Network Interfaces options ---------- + ------------------------------------------------ +*/ +/** + * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname + * field. + */ +#ifndef LWIP_NETIF_HOSTNAME +#define LWIP_NETIF_HOSTNAME 0 +#endif + +/** + * LWIP_NETIF_API==1: Support netif api (in netifapi.c) + */ +#ifndef LWIP_NETIF_API +#define LWIP_NETIF_API 0 +#endif + +/** + * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface + * changes its up/down status (i.e., due to DHCP IP acquistion) + */ +#ifndef LWIP_NETIF_STATUS_CALLBACK +#define LWIP_NETIF_STATUS_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface + * whenever the link changes (i.e., link down) + */ +#ifndef LWIP_NETIF_LINK_CALLBACK +#define LWIP_NETIF_LINK_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table + * indices) in struct netif. TCP and UDP can make use of this to prevent + * scanning the ARP table for every sent packet. While this is faster for big + * ARP tables or many concurrent connections, it might be counterproductive + * if you have a tiny ARP table or if there never are concurrent connections. + */ +#ifndef LWIP_NETIF_HWADDRHINT +#define LWIP_NETIF_HWADDRHINT 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP + * address equal to the netif IP address, looping them back up the stack. + */ +#ifndef LWIP_NETIF_LOOPBACK +#define LWIP_NETIF_LOOPBACK 1 +#endif + +/** + * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback + * sending for each netif (0 = disabled) + */ +#ifndef LWIP_LOOPBACK_MAX_PBUFS +#define LWIP_LOOPBACK_MAX_PBUFS 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in + * the system, as netifs must change how they behave depending on this setting + * for the LWIP_NETIF_LOOPBACK option to work. + * Setting this is needed to avoid reentering non-reentrant functions like + * tcp_input(). + * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a + * multithreaded environment like tcpip.c. In this case, netif->input() + * is called directly. + * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup. + * The packets are put on a list and netif_poll() must be called in + * the main application loop. + */ +#ifndef LWIP_NETIF_LOOPBACK_MULTITHREADING +#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS) +#endif + +/** + * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data + * to be sent into one single pbuf. This is for compatibility with DMA-enabled + * MACs that do not support scatter-gather. + * Beware that this might involve CPU-memcpy before transmitting that would not + * be needed without this flag! Use this only if you need to! + * + * @todo: TCP and IP-frag do not work with this, yet: + */ +#ifndef LWIP_NETIF_TX_SINGLE_PBUF +#define LWIP_NETIF_TX_SINGLE_PBUF 0 +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + +/* + ------------------------------------ + ---------- LOOPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1) and loopif.c + */ +#ifndef LWIP_HAVE_LOOPIF +#define LWIP_HAVE_LOOPIF 1 +#endif + +/* + ------------------------------------ + ---------- SLIPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_SLIPIF==1: Support slip interface and slipif.c + */ +#ifndef LWIP_HAVE_SLIPIF +#define LWIP_HAVE_SLIPIF 0 +#endif + +/* + ------------------------------------ + ---------- Thread options ---------- + ------------------------------------ +*/ +/** + * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread. + */ +#ifndef TCPIP_THREAD_NAME +#define TCPIP_THREAD_NAME "tcpip_thread" +#endif + +/** + * 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. + */ +#ifndef TCPIP_THREAD_STACKSIZE +#define TCPIP_THREAD_STACKSIZE 0 +#endif + +/** + * 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. + */ +#ifndef TCPIP_THREAD_PRIO +#define TCPIP_THREAD_PRIO 1 +#endif + +/** + * 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. + */ +#ifndef TCPIP_MBOX_SIZE +#define TCPIP_MBOX_SIZE 0 +#endif + +/** + * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread. + */ +#ifndef SLIPIF_THREAD_NAME +#define SLIPIF_THREAD_NAME "slipif_loop" +#endif + +/** + * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_STACKSIZE +#define SLIPIF_THREAD_STACKSIZE 0 +#endif + +/** + * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_PRIO +#define SLIPIF_THREAD_PRIO 1 +#endif + +/** + * PPP_THREAD_NAME: The name assigned to the pppInputThread. + */ +#ifndef PPP_THREAD_NAME +#define PPP_THREAD_NAME "pppInputThread" +#endif + +/** + * PPP_THREAD_STACKSIZE: The stack size used by the pppInputThread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_STACKSIZE +#define PPP_THREAD_STACKSIZE 0 +#endif + +/** + * PPP_THREAD_PRIO: The priority assigned to the pppInputThread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_PRIO +#define PPP_THREAD_PRIO 1 +#endif + +/** + * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread. + */ +#ifndef DEFAULT_THREAD_NAME +#define DEFAULT_THREAD_NAME "lwIP" +#endif + +/** + * 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. + */ +#ifndef DEFAULT_THREAD_STACKSIZE +#define DEFAULT_THREAD_STACKSIZE 0 +#endif + +/** + * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef DEFAULT_THREAD_PRIO +#define DEFAULT_THREAD_PRIO 1 +#endif + +/** + * 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. + */ +#ifndef DEFAULT_RAW_RECVMBOX_SIZE +#define DEFAULT_RAW_RECVMBOX_SIZE 0 +#endif + +/** + * 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. + */ +#ifndef DEFAULT_UDP_RECVMBOX_SIZE +#define DEFAULT_UDP_RECVMBOX_SIZE 0 +#endif + +/** + * 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. + */ +#ifndef DEFAULT_TCP_RECVMBOX_SIZE +#define DEFAULT_TCP_RECVMBOX_SIZE 0 +#endif + +/** + * 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. + */ +#ifndef DEFAULT_ACCEPTMBOX_SIZE +#define DEFAULT_ACCEPTMBOX_SIZE 0 +#endif + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * LWIP_TCPIP_CORE_LOCKING: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#ifndef LWIP_TCPIP_CORE_LOCKING +#define LWIP_TCPIP_CORE_LOCKING 0 +#endif + +/** + * LWIP_TCPIP_CORE_LOCKING_INPUT: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#ifndef LWIP_TCPIP_CORE_LOCKING_INPUT +#define LWIP_TCPIP_CORE_LOCKING_INPUT 0 +#endif + +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#ifndef LWIP_NETCONN +#define LWIP_NETCONN 0 +#endif + +/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout tod create + * timers running in tcpip_thread from another thread. + */ +#ifndef LWIP_TCPIP_TIMEOUT +#define LWIP_TCPIP_TIMEOUT 1 +#endif + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 0 +#endif + +/** + * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names. + * (only used if you use sockets.c) + */ +#ifndef LWIP_COMPAT_SOCKETS +#define LWIP_COMPAT_SOCKETS 1 +#endif + +/** + * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names. + * Disable this option if you use a POSIX operating system that uses the same + * names (read, write & close). (only used if you use sockets.c) + */ +#ifndef LWIP_POSIX_SOCKETS_IO_NAMES +#define LWIP_POSIX_SOCKETS_IO_NAMES 1 +#endif + +/** + * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT + * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set + * in seconds. (does not require sockets.c, and will affect tcp.c) + */ +#ifndef LWIP_TCP_KEEPALIVE +#define LWIP_TCP_KEEPALIVE 0 +#endif + +/** + * LWIP_SO_RCVTIMEO==1: Enable SO_RCVTIMEO processing. + */ +#ifndef LWIP_SO_RCVTIMEO +#define LWIP_SO_RCVTIMEO 0 +#endif + +/** + * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. + */ +#ifndef LWIP_SO_RCVBUF +#define LWIP_SO_RCVBUF 0 +#endif + +/** + * If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize. + */ +#ifndef RECV_BUFSIZE_DEFAULT +#define RECV_BUFSIZE_DEFAULT INT_MAX +#endif + +/** + * SO_REUSE==1: Enable SO_REUSEADDR option. + */ +#ifndef SO_REUSE +#define SO_REUSE 0 +#endif + +/** + * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets + * to all local matches if SO_REUSEADDR is turned on. + * WARNING: Adds a memcpy for every packet if passing to more than one pcb! + */ +#ifndef SO_REUSE_RXTOALL +#define SO_REUSE_RXTOALL 0 +#endif + +/* + ---------------------------------------- + ---------- Statistics options ---------- + ---------------------------------------- +*/ +/** + * LWIP_STATS==1: Enable statistics collection in lwip_stats. + */ +#ifndef LWIP_STATS +#define LWIP_STATS 1 +#endif + +#if LWIP_STATS + +/** + * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions. + */ +#ifndef LWIP_STATS_DISPLAY +#define LWIP_STATS_DISPLAY 0 +#endif + +/** + * LINK_STATS==1: Enable link stats. + */ +#ifndef LINK_STATS +#define LINK_STATS 1 +#endif + +/** + * ETHARP_STATS==1: Enable etharp stats. + */ +#ifndef ETHARP_STATS +#define ETHARP_STATS (LWIP_ARP) +#endif + +/** + * IP_STATS==1: Enable IP stats. + */ +#ifndef IP_STATS +#define IP_STATS 1 +#endif + +/** + * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is + * on if using either frag or reass. + */ +#ifndef IPFRAG_STATS +#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG) +#endif + +/** + * ICMP_STATS==1: Enable ICMP stats. + */ +#ifndef ICMP_STATS +#define ICMP_STATS 1 +#endif + +/** + * IGMP_STATS==1: Enable IGMP stats. + */ +#ifndef IGMP_STATS +#define IGMP_STATS (LWIP_IGMP) +#endif + +/** + * UDP_STATS==1: Enable UDP stats. Default is on if + * UDP enabled, otherwise off. + */ +#ifndef UDP_STATS +#define UDP_STATS (LWIP_UDP) +#endif + +/** + * TCP_STATS==1: Enable TCP stats. Default is on if TCP + * enabled, otherwise off. + */ +#ifndef TCP_STATS +#define TCP_STATS (LWIP_TCP) +#endif + +/** + * MEM_STATS==1: Enable mem.c stats. + */ +#ifndef MEM_STATS +#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0)) +#endif + +/** + * MEMP_STATS==1: Enable memp.c pool stats. + */ +#ifndef MEMP_STATS +#define MEMP_STATS (MEMP_MEM_MALLOC == 0) +#endif + +/** + * SYS_STATS==1: Enable system stats (sem and mbox counts, etc). + */ +#ifndef SYS_STATS +#define SYS_STATS (NO_SYS == 0) +#endif + +#else + +#define LINK_STATS 0 +#define IP_STATS 0 +#define IPFRAG_STATS 0 +#define ICMP_STATS 0 +#define IGMP_STATS 0 +#define UDP_STATS 0 +#define TCP_STATS 0 +#define MEM_STATS 0 +#define MEMP_STATS 0 +#define SYS_STATS 0 +#define LWIP_STATS_DISPLAY 0 + +#endif /* LWIP_STATS */ + +/* + --------------------------------- + ---------- PPP options ---------- + --------------------------------- +*/ +/** + * PPP_SUPPORT==1: Enable PPP. + */ +#ifndef PPP_SUPPORT +#define PPP_SUPPORT 0 +#endif + +/** + * PPPOE_SUPPORT==1: Enable PPP Over Ethernet + */ +#ifndef PPPOE_SUPPORT +#define PPPOE_SUPPORT 0 +#endif + +/** + * PPPOS_SUPPORT==1: Enable PPP Over Serial + */ +#ifndef PPPOS_SUPPORT +#define PPPOS_SUPPORT PPP_SUPPORT +#endif + +#if PPP_SUPPORT + +/** + * NUM_PPP: Max PPP sessions. + */ +#ifndef NUM_PPP +#define NUM_PPP 1 +#endif + +/** + * PAP_SUPPORT==1: Support PAP. + */ +#ifndef PAP_SUPPORT +#define PAP_SUPPORT 0 +#endif + +/** + * CHAP_SUPPORT==1: Support CHAP. + */ +#ifndef CHAP_SUPPORT +#define CHAP_SUPPORT 0 +#endif + +/** + * MSCHAP_SUPPORT==1: Support MSCHAP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef MSCHAP_SUPPORT +#define MSCHAP_SUPPORT 0 +#endif + +/** + * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CBCP_SUPPORT +#define CBCP_SUPPORT 0 +#endif + +/** + * CCP_SUPPORT==1: Support CCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CCP_SUPPORT +#define CCP_SUPPORT 0 +#endif + +/** + * VJ_SUPPORT==1: Support VJ header compression. + */ +#ifndef VJ_SUPPORT +#define VJ_SUPPORT 0 +#endif + +/** + * MD5_SUPPORT==1: Support MD5 (see also CHAP). + */ +#ifndef MD5_SUPPORT +#define MD5_SUPPORT 0 +#endif + +/* + * Timeouts + */ +#ifndef FSM_DEFTIMEOUT +#define FSM_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef FSM_DEFMAXTERMREQS +#define FSM_DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXCONFREQS +#define FSM_DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXNAKLOOPS +#define FSM_DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ +#endif + +#ifndef UPAP_DEFTIMEOUT +#define UPAP_DEFTIMEOUT 6 /* Timeout (seconds) for retransmitting req */ +#endif + +#ifndef UPAP_DEFREQTIME +#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif + +#ifndef CHAP_DEFTIMEOUT +#define CHAP_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef CHAP_DEFTRANSMITS +#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ +#endif + +/* Interval in seconds between keepalive echo requests, 0 to disable. */ +#ifndef LCP_ECHOINTERVAL +#define LCP_ECHOINTERVAL 0 +#endif + +/* Number of unanswered echo requests before failure. */ +#ifndef LCP_MAXECHOFAILS +#define LCP_MAXECHOFAILS 3 +#endif + +/* Max Xmit idle time (in jiffies) before resend flag char. */ +#ifndef PPP_MAXIDLEFLAG +#define PPP_MAXIDLEFLAG 100 +#endif + +/* + * Packet sizes + * + * Note - lcp shouldn't be allowed to negotiate stuff outside these + * limits. See lcp.h in the pppd directory. + * (XXX - these constants should simply be shared by lcp.c instead + * of living in lcp.h) + */ +#define PPP_MTU 1500 /* Default MTU (size of Info field) */ +#ifndef PPP_MAXMTU +/* #define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) */ +#define PPP_MAXMTU 1500 /* Largest MTU we allow */ +#endif +#define PPP_MINMTU 64 +#define PPP_MRU 1500 /* default MRU = max length of info field */ +#define PPP_MAXMRU 1500 /* Largest MRU we allow */ +#ifndef PPP_DEFMRU +#define PPP_DEFMRU 296 /* Try for this */ +#endif +#define PPP_MINMRU 128 /* No MRUs below this */ + +#ifndef MAXNAMELEN +#define MAXNAMELEN 256 /* max length of hostname or name for auth */ +#endif +#ifndef MAXSECRETLEN +#define MAXSECRETLEN 256 /* max length of password or secret */ +#endif + +#endif /* PPP_SUPPORT */ + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ +/** + * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets. + */ +#ifndef CHECKSUM_GEN_IP +#define CHECKSUM_GEN_IP 1 +#endif + +/** + * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets. + */ +#ifndef CHECKSUM_GEN_UDP +#define CHECKSUM_GEN_UDP 1 +#endif + +/** + * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets. + */ +#ifndef CHECKSUM_GEN_TCP +#define CHECKSUM_GEN_TCP 1 +#endif + +/** + * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets. + */ +#ifndef CHECKSUM_CHECK_IP +#define CHECKSUM_CHECK_IP 1 +#endif + +/** + * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets. + */ +#ifndef CHECKSUM_CHECK_UDP +#define CHECKSUM_CHECK_UDP 1 +#endif + +/** + * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets. + */ +#ifndef CHECKSUM_CHECK_TCP +#define CHECKSUM_CHECK_TCP 1 +#endif + +/** + * LWIP_CHECKSUM_ON_COPY==1: Calculate checksum when copying data from + * application buffers to pbufs. + */ +#ifndef LWIP_CHECKSUM_ON_COPY +#define LWIP_CHECKSUM_ON_COPY 0 +#endif + +/* + --------------------------------------- + ---------- Debugging options ---------- + --------------------------------------- +*/ +/** + * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is + * compared against this value. If it is smaller, then debugging + * messages are written. + */ +#ifndef LWIP_DBG_MIN_LEVEL +#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL +#endif + +/** + * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable + * debug messages of certain types. + */ +#ifndef LWIP_DBG_TYPES_ON +#define LWIP_DBG_TYPES_ON LWIP_DBG_ON +#endif + +/** + * ETHARP_DEBUG: Enable debugging in etharp.c. + */ +#ifndef ETHARP_DEBUG +#define ETHARP_DEBUG LWIP_DBG_OFF +#endif + +/** + * NETIF_DEBUG: Enable debugging in netif.c. + */ +#ifndef NETIF_DEBUG +#define NETIF_DEBUG LWIP_DBG_OFF +#endif + +/** + * PBUF_DEBUG: Enable debugging in pbuf.c. + */ +#ifndef PBUF_DEBUG +#define PBUF_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_LIB_DEBUG: Enable debugging in api_lib.c. + */ +#ifndef API_LIB_DEBUG +#define API_LIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_MSG_DEBUG: Enable debugging in api_msg.c. + */ +#ifndef API_MSG_DEBUG +#define API_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SOCKETS_DEBUG: Enable debugging in sockets.c. + */ +#ifndef SOCKETS_DEBUG +#define SOCKETS_DEBUG LWIP_DBG_OFF +#endif + +/** + * ICMP_DEBUG: Enable debugging in icmp.c. + */ +#ifndef ICMP_DEBUG +#define ICMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IGMP_DEBUG: Enable debugging in igmp.c. + */ +#ifndef IGMP_DEBUG +#define IGMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * INET_DEBUG: Enable debugging in inet.c. + */ +#ifndef INET_DEBUG +#define INET_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_DEBUG: Enable debugging for IP. + */ +#ifndef IP_DEBUG +#define IP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass. + */ +#ifndef IP_REASS_DEBUG +#define IP_REASS_DEBUG LWIP_DBG_OFF +#endif + +/** + * RAW_DEBUG: Enable debugging in raw.c. + */ +#ifndef RAW_DEBUG +#define RAW_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEM_DEBUG: Enable debugging in mem.c. + */ +#ifndef MEM_DEBUG +#define MEM_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEMP_DEBUG: Enable debugging in memp.c. + */ +#ifndef MEMP_DEBUG +#define MEMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SYS_DEBUG: Enable debugging in sys.c. + */ +#ifndef SYS_DEBUG +#define SYS_DEBUG LWIP_DBG_OFF +#endif + +/** + * TIMERS_DEBUG: Enable debugging in timers.c. + */ +#ifndef TIMERS_DEBUG +#define TIMERS_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_DEBUG: Enable debugging for TCP. + */ +#ifndef TCP_DEBUG +#define TCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug. + */ +#ifndef TCP_INPUT_DEBUG +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit. + */ +#ifndef TCP_FR_DEBUG +#define TCP_FR_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit + * timeout. + */ +#ifndef TCP_RTO_DEBUG +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_CWND_DEBUG: Enable debugging for TCP congestion window. + */ +#ifndef TCP_CWND_DEBUG +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating. + */ +#ifndef TCP_WND_DEBUG +#define TCP_WND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions. + */ +#ifndef TCP_OUTPUT_DEBUG +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RST_DEBUG: Enable debugging for TCP with the RST message. + */ +#ifndef TCP_RST_DEBUG +#define TCP_RST_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths. + */ +#ifndef TCP_QLEN_DEBUG +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#endif + +/** + * UDP_DEBUG: Enable debugging in UDP. + */ +#ifndef UDP_DEBUG +#define UDP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCPIP_DEBUG: Enable debugging in tcpip.c. + */ +#ifndef TCPIP_DEBUG +#define TCPIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * PPP_DEBUG: Enable debugging for PPP. + */ +#ifndef PPP_DEBUG +#define PPP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SLIP_DEBUG: Enable debugging in slipif.c. + */ +#ifndef SLIP_DEBUG +#define SLIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * DHCP_DEBUG: Enable debugging in dhcp.c. + */ +#ifndef DHCP_DEBUG +#define DHCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * AUTOIP_DEBUG: Enable debugging in autoip.c. + */ +#ifndef AUTOIP_DEBUG +#define AUTOIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MSG_DEBUG: Enable debugging for SNMP messages. + */ +#ifndef SNMP_MSG_DEBUG +#define SNMP_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs. + */ +#ifndef SNMP_MIB_DEBUG +#define SNMP_MIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * DNS_DEBUG: Enable debugging for DNS. + */ +#ifndef DNS_DEBUG +#define DNS_DEBUG LWIP_DBG_OFF +#endif + +#endif /* __LWIP_OPT_H__ */ diff --git a/app/include/lwip/pbuf.h b/app/include/lwip/pbuf.h new file mode 100644 index 00000000..3d24db4d --- /dev/null +++ b/app/include/lwip/pbuf.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __LWIP_PBUF_H__ +#define __LWIP_PBUF_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Currently, the pbuf_custom code is only needed for one specific configuration + * of IP_FRAG */ +#define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) + +#define PBUF_TRANSPORT_HLEN 20 +#define PBUF_IP_HLEN 20 + +typedef enum { + PBUF_TRANSPORT, + PBUF_IP, + PBUF_LINK, + PBUF_RAW +} pbuf_layer; + +typedef enum { + PBUF_RAM, /* pbuf data is stored in RAM */ + PBUF_ROM, /* pbuf data is stored in ROM */ + PBUF_REF, /* pbuf comes from the pbuf pool */ + PBUF_POOL, /* pbuf payload refers to RAM */ +#ifdef EBUF_LWIP + PBUF_ESF_RX /* pbuf payload is from WLAN */ +#endif /* ESF_LWIP */ +} pbuf_type; + + +/** indicates this packet's data should be immediately passed to the application */ +#define PBUF_FLAG_PUSH 0x01U +/** indicates this is a custom pbuf: pbuf_free and pbuf_header handle such a + a pbuf differently */ +#define PBUF_FLAG_IS_CUSTOM 0x02U +/** indicates this pbuf is UDP multicast to be looped back */ +#define PBUF_FLAG_MCASTLOOP 0x04U + +struct pbuf { + /** next pbuf in singly linked pbuf chain */ + struct pbuf *next; + + /** pointer to the actual data in the buffer */ + void *payload; + + /** + * total length of this buffer and all next buffers in chain + * belonging to the same packet. + * + * For non-queue packet chains this is the invariant: + * p->tot_len == p->len + (p->next? p->next->tot_len: 0) + */ + u16_t tot_len; + + /** length of this buffer */ + u16_t len; + + /** pbuf_type as u8_t instead of enum to save space */ + u8_t /*pbuf_type*/ type; + + /** misc flags */ + u8_t flags; + + /** + * the reference count always equals the number of pointers + * that refer to this pbuf. This can be pointers from an application, + * the stack itself, or pbuf->next pointers from a chain. + */ + u16_t ref; + + /* add a pointer for esf_buf */ + void * eb; +}; + +#if LWIP_SUPPORT_CUSTOM_PBUF +/** Prototype for a function to free a custom pbuf */ +typedef void (*pbuf_free_custom_fn)(struct pbuf *p); + +/** A custom pbuf: like a pbuf, but following a function pointer to free it. */ +struct pbuf_custom { + /** The actual pbuf */ + struct pbuf pbuf; + /** This function is called when pbuf_free deallocates this pbuf(_custom) */ + pbuf_free_custom_fn custom_free_function; +}; +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + +/* Initializes the pbuf module. This call is empty for now, but may not be in future. */ +#define pbuf_init() + +struct pbuf *pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type)ICACHE_FLASH_ATTR; +#if LWIP_SUPPORT_CUSTOM_PBUF +struct pbuf *pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, + struct pbuf_custom *p, void *payload_mem, + u16_t payload_mem_len)ICACHE_FLASH_ATTR; +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ +void pbuf_realloc(struct pbuf *p, u16_t size)ICACHE_FLASH_ATTR; +u8_t pbuf_header(struct pbuf *p, s16_t header_size)ICACHE_FLASH_ATTR; +void pbuf_ref(struct pbuf *p)ICACHE_FLASH_ATTR; +u8_t pbuf_free(struct pbuf *p)ICACHE_FLASH_ATTR; +u8_t pbuf_clen(struct pbuf *p)ICACHE_FLASH_ATTR; +void pbuf_cat(struct pbuf *head, struct pbuf *tail)ICACHE_FLASH_ATTR; +void pbuf_chain(struct pbuf *head, struct pbuf *tail)ICACHE_FLASH_ATTR; +struct pbuf *pbuf_dechain(struct pbuf *p)ICACHE_FLASH_ATTR; +err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)ICACHE_FLASH_ATTR; +u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset)ICACHE_FLASH_ATTR; +err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)ICACHE_FLASH_ATTR; +struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer)ICACHE_FLASH_ATTR; +#if LWIP_CHECKSUM_ON_COPY +err_t pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, + u16_t len, u16_t *chksum)ICACHE_FLASH_ATTR; +#endif /* LWIP_CHECKSUM_ON_COPY */ + +u8_t pbuf_get_at(struct pbuf* p, u16_t offset)ICACHE_FLASH_ATTR; +u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)ICACHE_FLASH_ATTR; +u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)ICACHE_FLASH_ATTR; +u16_t pbuf_strstr(struct pbuf* p, const char* substr)ICACHE_FLASH_ATTR; + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_PBUF_H__ */ diff --git a/app/include/lwip/raw.h b/app/include/lwip/raw.h new file mode 100644 index 00000000..c9c40871 --- /dev/null +++ b/app/include/lwip/raw.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_RAW_H__ +#define __LWIP_RAW_H__ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/def.h" +#include "lwip/ip.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct raw_pcb; + +/** Function prototype for raw pcb receive callback functions. + * @param arg user supplied argument (raw_pcb.recv_arg) + * @param pcb the raw_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @return 1 if the packet was 'eaten' (aka. deleted), + * 0 if the packet lives on + * If returning 1, the callback is responsible for freeing the pbuf + * if it's not used any more. + */ +typedef u8_t (*raw_recv_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip_addr_t *addr); + +struct raw_pcb { + /* Common members of all PCB types */ + IP_PCB; + + struct raw_pcb *next; + + u8_t protocol; + + /** receive callback function */ + raw_recv_fn recv; + /* user-supplied argument for the recv callback */ + void *recv_arg; +}; + +/* The following functions is the application layer interface to the + RAW code. */ +struct raw_pcb * raw_new (u8_t proto)ICACHE_FLASH_ATTR; +void raw_remove (struct raw_pcb *pcb)ICACHE_FLASH_ATTR; +err_t raw_bind (struct raw_pcb *pcb, ip_addr_t *ipaddr)ICACHE_FLASH_ATTR; +err_t raw_connect (struct raw_pcb *pcb, ip_addr_t *ipaddr)ICACHE_FLASH_ATTR; + +void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)ICACHE_FLASH_ATTR; +err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)ICACHE_FLASH_ATTR; +err_t raw_send (struct raw_pcb *pcb, struct pbuf *p); + +/* The following functions are the lower layer interface to RAW. */ +u8_t raw_input (struct pbuf *p, struct netif *inp)ICACHE_FLASH_ATTR; +#define raw_init() /* Compatibility define, not init needed. */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_RAW */ + +#endif /* __LWIP_RAW_H__ */ diff --git a/app/include/lwip/sio.h b/app/include/lwip/sio.h new file mode 100644 index 00000000..228c8577 --- /dev/null +++ b/app/include/lwip/sio.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + */ + +/* + * This is the interface to the platform specific serial IO module + * It needs to be implemented by those platforms which need SLIP or PPP + */ + +#ifndef __SIO_H__ +#define __SIO_H__ + +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* If you want to define sio_fd_t elsewhere or differently, + define this in your cc.h file. */ +#ifndef __sio_fd_t_defined +typedef void * sio_fd_t; +#endif + +/* The following functions can be defined to something else in your cc.h file + or be implemented in your custom sio.c file. */ + +#ifndef sio_open +/** + * Opens a serial device for communication. + * + * @param devnum device number + * @return handle to serial device if successful, NULL otherwise + */ +sio_fd_t sio_open(u8_t devnum)ICACHE_FLASH_ATTR; +#endif + +#ifndef sio_send +/** + * Sends a single character to the serial device. + * + * @param c character to send + * @param fd serial device handle + * + * @note This function will block until the character can be sent. + */ +void sio_send(u8_t c, sio_fd_t fd)ICACHE_FLASH_ATTR; +#endif + +#ifndef sio_recv +/** + * Receives a single character from the serial device. + * + * @param fd serial device handle + * + * @note This function will block until a character is received. + */ +u8_t sio_recv(sio_fd_t fd)ICACHE_FLASH_ATTR; +#endif + +#ifndef sio_read +/** + * Reads from the serial device. + * + * @param fd serial device handle + * @param data pointer to data buffer for receiving + * @param len maximum length (in bytes) of data to receive + * @return number of bytes actually received - may be 0 if aborted by sio_read_abort + * + * @note This function will block until data can be received. The blocking + * can be cancelled by calling sio_read_abort(). + */ +u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len)ICACHE_FLASH_ATTR; +#endif + +#ifndef sio_tryread +/** + * Tries to read from the serial device. Same as sio_read but returns + * immediately if no data is available and never blocks. + * + * @param fd serial device handle + * @param data pointer to data buffer for receiving + * @param len maximum length (in bytes) of data to receive + * @return number of bytes actually received + */ +u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len)ICACHE_FLASH_ATTR; +#endif + +#ifndef sio_write +/** + * Writes to the serial device. + * + * @param fd serial device handle + * @param data pointer to data to send + * @param len length (in bytes) of data to send + * @return number of bytes actually sent + * + * @note This function will block until all data can be sent. + */ +u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len)ICACHE_FLASH_ATTR; +#endif + +#ifndef sio_read_abort +/** + * Aborts a blocking sio_read() call. + * + * @param fd serial device handle + */ +void sio_read_abort(sio_fd_t fd)ICACHE_FLASH_ATTR; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __SIO_H__ */ diff --git a/app/include/lwip/snmp.h b/app/include/lwip/snmp.h new file mode 100644 index 00000000..2ed043dd --- /dev/null +++ b/app/include/lwip/snmp.h @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2001, 2002 Leon Woestenberg + * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Leon Woestenberg + * + */ +#ifndef __LWIP_SNMP_H__ +#define __LWIP_SNMP_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwip/ip_addr.h" + +struct udp_pcb; +struct netif; + +/** + * @see RFC1213, "MIB-II, 6. Definitions" + */ +enum snmp_ifType { + snmp_ifType_other=1, /* none of the following */ + snmp_ifType_regular1822, + snmp_ifType_hdh1822, + snmp_ifType_ddn_x25, + snmp_ifType_rfc877_x25, + snmp_ifType_ethernet_csmacd, + snmp_ifType_iso88023_csmacd, + snmp_ifType_iso88024_tokenBus, + snmp_ifType_iso88025_tokenRing, + snmp_ifType_iso88026_man, + snmp_ifType_starLan, + snmp_ifType_proteon_10Mbit, + snmp_ifType_proteon_80Mbit, + snmp_ifType_hyperchannel, + snmp_ifType_fddi, + snmp_ifType_lapb, + snmp_ifType_sdlc, + snmp_ifType_ds1, /* T-1 */ + snmp_ifType_e1, /* european equiv. of T-1 */ + snmp_ifType_basicISDN, + snmp_ifType_primaryISDN, /* proprietary serial */ + snmp_ifType_propPointToPointSerial, + snmp_ifType_ppp, + snmp_ifType_softwareLoopback, + snmp_ifType_eon, /* CLNP over IP [11] */ + snmp_ifType_ethernet_3Mbit, + snmp_ifType_nsip, /* XNS over IP */ + snmp_ifType_slip, /* generic SLIP */ + snmp_ifType_ultra, /* ULTRA technologies */ + snmp_ifType_ds3, /* T-3 */ + snmp_ifType_sip, /* SMDS */ + snmp_ifType_frame_relay +}; + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +/** SNMP "sysuptime" Interval */ +#define SNMP_SYSUPTIME_INTERVAL 10 + +/** fixed maximum length for object identifier type */ +#define LWIP_SNMP_OBJ_ID_LEN 32 + +/** internal object identifier representation */ +struct snmp_obj_id +{ + u8_t len; + s32_t id[LWIP_SNMP_OBJ_ID_LEN]; +}; + +/* system */ +void snmp_set_sysdesr(u8_t* str, u8_t* len); +void snmp_set_sysobjid(struct snmp_obj_id *oid); +void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid); +void snmp_inc_sysuptime(void); +void snmp_add_sysuptime(u32_t value); +void snmp_get_sysuptime(u32_t *value); +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen); +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen); +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen); + +/* network interface */ +void snmp_add_ifinoctets(struct netif *ni, u32_t value); +void snmp_inc_ifinucastpkts(struct netif *ni); +void snmp_inc_ifinnucastpkts(struct netif *ni); +void snmp_inc_ifindiscards(struct netif *ni); +void snmp_add_ifoutoctets(struct netif *ni, u32_t value); +void snmp_inc_ifoutucastpkts(struct netif *ni); +void snmp_inc_ifoutnucastpkts(struct netif *ni); +void snmp_inc_ifoutdiscards(struct netif *ni); +void snmp_inc_iflist(void); +void snmp_dec_iflist(void); + +/* ARP (for atTable and ipNetToMediaTable) */ +void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip); +void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip); + +/* IP */ +void snmp_inc_ipinreceives(void); +void snmp_inc_ipinhdrerrors(void); +void snmp_inc_ipinaddrerrors(void); +void snmp_inc_ipforwdatagrams(void); +void snmp_inc_ipinunknownprotos(void); +void snmp_inc_ipindiscards(void); +void snmp_inc_ipindelivers(void); +void snmp_inc_ipoutrequests(void); +void snmp_inc_ipoutdiscards(void); +void snmp_inc_ipoutnoroutes(void); +void snmp_inc_ipreasmreqds(void); +void snmp_inc_ipreasmoks(void); +void snmp_inc_ipreasmfails(void); +void snmp_inc_ipfragoks(void); +void snmp_inc_ipfragfails(void); +void snmp_inc_ipfragcreates(void); +void snmp_inc_iproutingdiscards(void); +void snmp_insert_ipaddridx_tree(struct netif *ni); +void snmp_delete_ipaddridx_tree(struct netif *ni); +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni); +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni); + +/* ICMP */ +void snmp_inc_icmpinmsgs(void); +void snmp_inc_icmpinerrors(void); +void snmp_inc_icmpindestunreachs(void); +void snmp_inc_icmpintimeexcds(void); +void snmp_inc_icmpinparmprobs(void); +void snmp_inc_icmpinsrcquenchs(void); +void snmp_inc_icmpinredirects(void); +void snmp_inc_icmpinechos(void); +void snmp_inc_icmpinechoreps(void); +void snmp_inc_icmpintimestamps(void); +void snmp_inc_icmpintimestampreps(void); +void snmp_inc_icmpinaddrmasks(void); +void snmp_inc_icmpinaddrmaskreps(void); +void snmp_inc_icmpoutmsgs(void); +void snmp_inc_icmpouterrors(void); +void snmp_inc_icmpoutdestunreachs(void); +void snmp_inc_icmpouttimeexcds(void); +void snmp_inc_icmpoutparmprobs(void); +void snmp_inc_icmpoutsrcquenchs(void); +void snmp_inc_icmpoutredirects(void); +void snmp_inc_icmpoutechos(void); +void snmp_inc_icmpoutechoreps(void); +void snmp_inc_icmpouttimestamps(void); +void snmp_inc_icmpouttimestampreps(void); +void snmp_inc_icmpoutaddrmasks(void); +void snmp_inc_icmpoutaddrmaskreps(void); + +/* TCP */ +void snmp_inc_tcpactiveopens(void); +void snmp_inc_tcppassiveopens(void); +void snmp_inc_tcpattemptfails(void); +void snmp_inc_tcpestabresets(void); +void snmp_inc_tcpinsegs(void); +void snmp_inc_tcpoutsegs(void); +void snmp_inc_tcpretranssegs(void); +void snmp_inc_tcpinerrs(void); +void snmp_inc_tcpoutrsts(void); + +/* UDP */ +void snmp_inc_udpindatagrams(void); +void snmp_inc_udpnoports(void); +void snmp_inc_udpinerrors(void); +void snmp_inc_udpoutdatagrams(void); +void snmp_insert_udpidx_tree(struct udp_pcb *pcb); +void snmp_delete_udpidx_tree(struct udp_pcb *pcb); + +/* SNMP */ +void snmp_inc_snmpinpkts(void); +void snmp_inc_snmpoutpkts(void); +void snmp_inc_snmpinbadversions(void); +void snmp_inc_snmpinbadcommunitynames(void); +void snmp_inc_snmpinbadcommunityuses(void); +void snmp_inc_snmpinasnparseerrs(void); +void snmp_inc_snmpintoobigs(void); +void snmp_inc_snmpinnosuchnames(void); +void snmp_inc_snmpinbadvalues(void); +void snmp_inc_snmpinreadonlys(void); +void snmp_inc_snmpingenerrs(void); +void snmp_add_snmpintotalreqvars(u8_t value); +void snmp_add_snmpintotalsetvars(u8_t value); +void snmp_inc_snmpingetrequests(void); +void snmp_inc_snmpingetnexts(void); +void snmp_inc_snmpinsetrequests(void); +void snmp_inc_snmpingetresponses(void); +void snmp_inc_snmpintraps(void); +void snmp_inc_snmpouttoobigs(void); +void snmp_inc_snmpoutnosuchnames(void); +void snmp_inc_snmpoutbadvalues(void); +void snmp_inc_snmpoutgenerrs(void); +void snmp_inc_snmpoutgetrequests(void); +void snmp_inc_snmpoutgetnexts(void); +void snmp_inc_snmpoutsetrequests(void); +void snmp_inc_snmpoutgetresponses(void); +void snmp_inc_snmpouttraps(void); +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid); +void snmp_set_snmpenableauthentraps(u8_t *value); +void snmp_get_snmpenableauthentraps(u8_t *value); + +/* LWIP_SNMP support not available */ +/* define everything to be empty */ +#else + +/* system */ +#define snmp_set_sysdesr(str, len) +#define snmp_set_sysobjid(oid); +#define snmp_get_sysobjid_ptr(oid) +#define snmp_inc_sysuptime() +#define snmp_add_sysuptime(value) +#define snmp_get_sysuptime(value) +#define snmp_set_syscontact(ocstr, ocstrlen); +#define snmp_set_sysname(ocstr, ocstrlen); +#define snmp_set_syslocation(ocstr, ocstrlen); + +/* network interface */ +#define snmp_add_ifinoctets(ni,value) +#define snmp_inc_ifinucastpkts(ni) +#define snmp_inc_ifinnucastpkts(ni) +#define snmp_inc_ifindiscards(ni) +#define snmp_add_ifoutoctets(ni,value) +#define snmp_inc_ifoutucastpkts(ni) +#define snmp_inc_ifoutnucastpkts(ni) +#define snmp_inc_ifoutdiscards(ni) +#define snmp_inc_iflist() +#define snmp_dec_iflist() + +/* ARP */ +#define snmp_insert_arpidx_tree(ni,ip) +#define snmp_delete_arpidx_tree(ni,ip) + +/* IP */ +#define snmp_inc_ipinreceives() +#define snmp_inc_ipinhdrerrors() +#define snmp_inc_ipinaddrerrors() +#define snmp_inc_ipforwdatagrams() +#define snmp_inc_ipinunknownprotos() +#define snmp_inc_ipindiscards() +#define snmp_inc_ipindelivers() +#define snmp_inc_ipoutrequests() +#define snmp_inc_ipoutdiscards() +#define snmp_inc_ipoutnoroutes() +#define snmp_inc_ipreasmreqds() +#define snmp_inc_ipreasmoks() +#define snmp_inc_ipreasmfails() +#define snmp_inc_ipfragoks() +#define snmp_inc_ipfragfails() +#define snmp_inc_ipfragcreates() +#define snmp_inc_iproutingdiscards() +#define snmp_insert_ipaddridx_tree(ni) +#define snmp_delete_ipaddridx_tree(ni) +#define snmp_insert_iprteidx_tree(dflt, ni) +#define snmp_delete_iprteidx_tree(dflt, ni) + +/* ICMP */ +#define snmp_inc_icmpinmsgs() +#define snmp_inc_icmpinerrors() +#define snmp_inc_icmpindestunreachs() +#define snmp_inc_icmpintimeexcds() +#define snmp_inc_icmpinparmprobs() +#define snmp_inc_icmpinsrcquenchs() +#define snmp_inc_icmpinredirects() +#define snmp_inc_icmpinechos() +#define snmp_inc_icmpinechoreps() +#define snmp_inc_icmpintimestamps() +#define snmp_inc_icmpintimestampreps() +#define snmp_inc_icmpinaddrmasks() +#define snmp_inc_icmpinaddrmaskreps() +#define snmp_inc_icmpoutmsgs() +#define snmp_inc_icmpouterrors() +#define snmp_inc_icmpoutdestunreachs() +#define snmp_inc_icmpouttimeexcds() +#define snmp_inc_icmpoutparmprobs() +#define snmp_inc_icmpoutsrcquenchs() +#define snmp_inc_icmpoutredirects() +#define snmp_inc_icmpoutechos() +#define snmp_inc_icmpoutechoreps() +#define snmp_inc_icmpouttimestamps() +#define snmp_inc_icmpouttimestampreps() +#define snmp_inc_icmpoutaddrmasks() +#define snmp_inc_icmpoutaddrmaskreps() +/* TCP */ +#define snmp_inc_tcpactiveopens() +#define snmp_inc_tcppassiveopens() +#define snmp_inc_tcpattemptfails() +#define snmp_inc_tcpestabresets() +#define snmp_inc_tcpinsegs() +#define snmp_inc_tcpoutsegs() +#define snmp_inc_tcpretranssegs() +#define snmp_inc_tcpinerrs() +#define snmp_inc_tcpoutrsts() + +/* UDP */ +#define snmp_inc_udpindatagrams() +#define snmp_inc_udpnoports() +#define snmp_inc_udpinerrors() +#define snmp_inc_udpoutdatagrams() +#define snmp_insert_udpidx_tree(pcb) +#define snmp_delete_udpidx_tree(pcb) + +/* SNMP */ +#define snmp_inc_snmpinpkts() +#define snmp_inc_snmpoutpkts() +#define snmp_inc_snmpinbadversions() +#define snmp_inc_snmpinbadcommunitynames() +#define snmp_inc_snmpinbadcommunityuses() +#define snmp_inc_snmpinasnparseerrs() +#define snmp_inc_snmpintoobigs() +#define snmp_inc_snmpinnosuchnames() +#define snmp_inc_snmpinbadvalues() +#define snmp_inc_snmpinreadonlys() +#define snmp_inc_snmpingenerrs() +#define snmp_add_snmpintotalreqvars(value) +#define snmp_add_snmpintotalsetvars(value) +#define snmp_inc_snmpingetrequests() +#define snmp_inc_snmpingetnexts() +#define snmp_inc_snmpinsetrequests() +#define snmp_inc_snmpingetresponses() +#define snmp_inc_snmpintraps() +#define snmp_inc_snmpouttoobigs() +#define snmp_inc_snmpoutnosuchnames() +#define snmp_inc_snmpoutbadvalues() +#define snmp_inc_snmpoutgenerrs() +#define snmp_inc_snmpoutgetrequests() +#define snmp_inc_snmpoutgetnexts() +#define snmp_inc_snmpoutsetrequests() +#define snmp_inc_snmpoutgetresponses() +#define snmp_inc_snmpouttraps() +#define snmp_get_snmpgrpid_ptr(oid) +#define snmp_set_snmpenableauthentraps(value) +#define snmp_get_snmpenableauthentraps(value) + +#endif /* LWIP_SNMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_SNMP_H__ */ diff --git a/app/include/lwip/snmp_asn1.h b/app/include/lwip/snmp_asn1.h new file mode 100644 index 00000000..605fa3f1 --- /dev/null +++ b/app/include/lwip/snmp_asn1.h @@ -0,0 +1,101 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) codec. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_ASN1_H__ +#define __LWIP_SNMP_ASN1_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/snmp.h" + +#if LWIP_SNMP + +#ifdef __cplusplus +extern "C" { +#endif + +#define SNMP_ASN1_UNIV (0) /* (!0x80 | !0x40) */ +#define SNMP_ASN1_APPLIC (0x40) /* (!0x80 | 0x40) */ +#define SNMP_ASN1_CONTXT (0x80) /* ( 0x80 | !0x40) */ + +#define SNMP_ASN1_CONSTR (0x20) /* ( 0x20) */ +#define SNMP_ASN1_PRIMIT (0) /* (!0x20) */ + +/* universal tags */ +#define SNMP_ASN1_INTEG 2 +#define SNMP_ASN1_OC_STR 4 +#define SNMP_ASN1_NUL 5 +#define SNMP_ASN1_OBJ_ID 6 +#define SNMP_ASN1_SEQ 16 + +/* application specific (SNMP) tags */ +#define SNMP_ASN1_IPADDR 0 /* octet string size(4) */ +#define SNMP_ASN1_COUNTER 1 /* u32_t */ +#define SNMP_ASN1_GAUGE 2 /* u32_t */ +#define SNMP_ASN1_TIMETICKS 3 /* u32_t */ +#define SNMP_ASN1_OPAQUE 4 /* octet string */ + +/* context specific (SNMP) tags */ +#define SNMP_ASN1_PDU_GET_REQ 0 +#define SNMP_ASN1_PDU_GET_NEXT_REQ 1 +#define SNMP_ASN1_PDU_GET_RESP 2 +#define SNMP_ASN1_PDU_SET_REQ 3 +#define SNMP_ASN1_PDU_TRAP 4 + +err_t snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type); +err_t snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length); +err_t snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value); +err_t snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value); +err_t snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid); +err_t snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw); + +void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed); +void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed); +void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed); +void snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed); +err_t snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type); +err_t snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length); +err_t snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value); +err_t snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value); +err_t snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident); +err_t snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_ASN1_H__ */ diff --git a/app/include/lwip/snmp_msg.h b/app/include/lwip/snmp_msg.h new file mode 100644 index 00000000..1183e3a9 --- /dev/null +++ b/app/include/lwip/snmp_msg.h @@ -0,0 +1,315 @@ +/** + * @file + * SNMP Agent message handling structures. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_MSG_H__ +#define __LWIP_SNMP_MSG_H__ + +#include "lwip/opt.h" +#include "lwip/snmp.h" +#include "lwip/snmp_structs.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#if LWIP_SNMP + +#if SNMP_PRIVATE_MIB +/* When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. */ +#include "private_mib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* The listen port of the SNMP agent. Clients have to make their requests to + this port. Most standard clients won't work if you change this! */ +#ifndef SNMP_IN_PORT +#define SNMP_IN_PORT 161 +#endif +/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't + work if you change this! */ +#ifndef SNMP_TRAP_PORT +#define SNMP_TRAP_PORT 162 +#endif + +#define SNMP_ES_NOERROR 0 +#define SNMP_ES_TOOBIG 1 +#define SNMP_ES_NOSUCHNAME 2 +#define SNMP_ES_BADVALUE 3 +#define SNMP_ES_READONLY 4 +#define SNMP_ES_GENERROR 5 + +#define SNMP_GENTRAP_COLDSTART 0 +#define SNMP_GENTRAP_WARMSTART 1 +#define SNMP_GENTRAP_AUTHFAIL 4 +#define SNMP_GENTRAP_ENTERPRISESPC 6 + +struct snmp_varbind +{ + /* next pointer, NULL for last in list */ + struct snmp_varbind *next; + /* previous pointer, NULL for first in list */ + struct snmp_varbind *prev; + + /* object identifier length (in s32_t) */ + u8_t ident_len; + /* object identifier array */ + s32_t *ident; + + /* object value ASN1 type */ + u8_t value_type; + /* object value length (in u8_t) */ + u8_t value_len; + /* object value */ + void *value; + + /* encoding varbind seq length length */ + u8_t seqlenlen; + /* encoding object identifier length length */ + u8_t olenlen; + /* encoding object value length length */ + u8_t vlenlen; + /* encoding varbind seq length */ + u16_t seqlen; + /* encoding object identifier length */ + u16_t olen; + /* encoding object value length */ + u16_t vlen; +}; + +struct snmp_varbind_root +{ + struct snmp_varbind *head; + struct snmp_varbind *tail; + /* number of variable bindings in list */ + u8_t count; + /* encoding varbind-list seq length length */ + u8_t seqlenlen; + /* encoding varbind-list seq length */ + u16_t seqlen; +}; + +/** output response message header length fields */ +struct snmp_resp_header_lengths +{ + /* encoding error-index length length */ + u8_t erridxlenlen; + /* encoding error-status length length */ + u8_t errstatlenlen; + /* encoding request id length length */ + u8_t ridlenlen; + /* encoding pdu length length */ + u8_t pdulenlen; + /* encoding community length length */ + u8_t comlenlen; + /* encoding version length length */ + u8_t verlenlen; + /* encoding sequence length length */ + u8_t seqlenlen; + + /* encoding error-index length */ + u16_t erridxlen; + /* encoding error-status length */ + u16_t errstatlen; + /* encoding request id length */ + u16_t ridlen; + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding version length */ + u16_t verlen; + /* encoding sequence length */ + u16_t seqlen; +}; + +/** output response message header length fields */ +struct snmp_trap_header_lengths +{ + /* encoding timestamp length length */ + u8_t tslenlen; + /* encoding specific-trap length length */ + u8_t strplenlen; + /* encoding generic-trap length length */ + u8_t gtrplenlen; + /* encoding agent-addr length length */ + u8_t aaddrlenlen; + /* encoding enterprise-id length length */ + u8_t eidlenlen; + /* encoding pdu length length */ + u8_t pdulenlen; + /* encoding community length length */ + u8_t comlenlen; + /* encoding version length length */ + u8_t verlenlen; + /* encoding sequence length length */ + u8_t seqlenlen; + + /* encoding timestamp length */ + u16_t tslen; + /* encoding specific-trap length */ + u16_t strplen; + /* encoding generic-trap length */ + u16_t gtrplen; + /* encoding agent-addr length */ + u16_t aaddrlen; + /* encoding enterprise-id length */ + u16_t eidlen; + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding version length */ + u16_t verlen; + /* encoding sequence length */ + u16_t seqlen; +}; + +/* Accepting new SNMP messages. */ +#define SNMP_MSG_EMPTY 0 +/* Search for matching object for variable binding. */ +#define SNMP_MSG_SEARCH_OBJ 1 +/* Perform SNMP operation on in-memory object. + Pass-through states, for symmetry only. */ +#define SNMP_MSG_INTERNAL_GET_OBJDEF 2 +#define SNMP_MSG_INTERNAL_GET_VALUE 3 +#define SNMP_MSG_INTERNAL_SET_TEST 4 +#define SNMP_MSG_INTERNAL_GET_OBJDEF_S 5 +#define SNMP_MSG_INTERNAL_SET_VALUE 6 +/* Perform SNMP operation on object located externally. + In theory this could be used for building a proxy agent. + Practical use is for an enterprise spc. app. gateway. */ +#define SNMP_MSG_EXTERNAL_GET_OBJDEF 7 +#define SNMP_MSG_EXTERNAL_GET_VALUE 8 +#define SNMP_MSG_EXTERNAL_SET_TEST 9 +#define SNMP_MSG_EXTERNAL_GET_OBJDEF_S 10 +#define SNMP_MSG_EXTERNAL_SET_VALUE 11 + +#define SNMP_COMMUNITY_STR_LEN 64 +struct snmp_msg_pstat +{ + /* lwIP local port (161) binding */ + struct udp_pcb *pcb; + /* source IP address */ + ip_addr_t sip; + /* source UDP port */ + u16_t sp; + /* request type */ + u8_t rt; + /* request ID */ + s32_t rid; + /* error status */ + s32_t error_status; + /* error index */ + s32_t error_index; + /* community name (zero terminated) */ + u8_t community[SNMP_COMMUNITY_STR_LEN + 1]; + /* community string length (exclusive zero term) */ + u8_t com_strlen; + /* one out of MSG_EMPTY, MSG_DEMUX, MSG_INTERNAL, MSG_EXTERNAL_x */ + u8_t state; + /* saved arguments for MSG_EXTERNAL_x */ + struct mib_external_node *ext_mib_node; + struct snmp_name_ptr ext_name_ptr; + struct obj_def ext_object_def; + struct snmp_obj_id ext_oid; + /* index into input variable binding list */ + u8_t vb_idx; + /* ptr into input variable binding list */ + struct snmp_varbind *vb_ptr; + /* list of variable bindings from input */ + struct snmp_varbind_root invb; + /* list of variable bindings to output */ + struct snmp_varbind_root outvb; + /* output response lengths used in ASN encoding */ + struct snmp_resp_header_lengths rhl; +}; + +struct snmp_msg_trap +{ + /* lwIP local port (161) binding */ + struct udp_pcb *pcb; + /* destination IP address in network order */ + ip_addr_t dip; + + /* source enterprise ID (sysObjectID) */ + struct snmp_obj_id *enterprise; + /* source IP address, raw network order format */ + u8_t sip_raw[4]; + /* generic trap code */ + u32_t gen_trap; + /* specific trap code */ + u32_t spc_trap; + /* timestamp */ + u32_t ts; + /* list of variable bindings to output */ + struct snmp_varbind_root outvb; + /* output trap lengths used in ASN encoding */ + struct snmp_trap_header_lengths thl; +}; + +/** Agent Version constant, 0 = v1 oddity */ +extern const s32_t snmp_version; +/** Agent default "public" community string */ +extern const char snmp_publiccommunity[7]; + +extern struct snmp_msg_trap trap_msg; + +/** Agent setup, start listening to port 161. */ +void snmp_init(void); +void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable); +void snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst); + +/** Varbind-list functions. */ +struct snmp_varbind* snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len); +void snmp_varbind_free(struct snmp_varbind *vb); +void snmp_varbind_list_free(struct snmp_varbind_root *root); +void snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb); +struct snmp_varbind* snmp_varbind_tail_remove(struct snmp_varbind_root *root); + +/** Handle an internal (recv) or external (private response) event. */ +void snmp_msg_event(u8_t request_id); +err_t snmp_send_response(struct snmp_msg_pstat *m_stat); +err_t snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap); +void snmp_coldstart_trap(void); +void snmp_authfail_trap(void); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_MSG_H__ */ diff --git a/app/include/lwip/snmp_structs.h b/app/include/lwip/snmp_structs.h new file mode 100644 index 00000000..0d3b46a9 --- /dev/null +++ b/app/include/lwip/snmp_structs.h @@ -0,0 +1,268 @@ +/** + * @file + * Generic MIB tree structures. + * + * @todo namespace prefixes + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_STRUCTS_H__ +#define __LWIP_SNMP_STRUCTS_H__ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" + +#if SNMP_PRIVATE_MIB +/* When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. */ +#include "private_mib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* MIB object instance */ +#define MIB_OBJECT_NONE 0 +#define MIB_OBJECT_SCALAR 1 +#define MIB_OBJECT_TAB 2 + +/* MIB access types */ +#define MIB_ACCESS_READ 1 +#define MIB_ACCESS_WRITE 2 + +/* MIB object access */ +#define MIB_OBJECT_READ_ONLY MIB_ACCESS_READ +#define MIB_OBJECT_READ_WRITE (MIB_ACCESS_READ | MIB_ACCESS_WRITE) +#define MIB_OBJECT_WRITE_ONLY MIB_ACCESS_WRITE +#define MIB_OBJECT_NOT_ACCESSIBLE 0 + +/** object definition returned by (get_object_def)() */ +struct obj_def +{ + /* MIB_OBJECT_NONE (0), MIB_OBJECT_SCALAR (1), MIB_OBJECT_TAB (2) */ + u8_t instance; + /* 0 read-only, 1 read-write, 2 write-only, 3 not-accessible */ + u8_t access; + /* ASN type for this object */ + u8_t asn_type; + /* value length (host length) */ + u16_t v_len; + /* length of instance part of supplied object identifier */ + u8_t id_inst_len; + /* instance part of supplied object identifier */ + s32_t *id_inst_ptr; +}; + +struct snmp_name_ptr +{ + u8_t ident_len; + s32_t *ident; +}; + +/** MIB const scalar (.0) node */ +#define MIB_NODE_SC 0x01 +/** MIB const array node */ +#define MIB_NODE_AR 0x02 +/** MIB array node (mem_malloced from RAM) */ +#define MIB_NODE_RA 0x03 +/** MIB list root node (mem_malloced from RAM) */ +#define MIB_NODE_LR 0x04 +/** MIB node for external objects */ +#define MIB_NODE_EX 0x05 + +/** node "base class" layout, the mandatory fields for a node */ +struct mib_node +{ + /** returns struct obj_def for the given object identifier */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + /** returns object value for the given object identifier, + @note the caller must allocate at least len bytes for the value */ + void (*get_value)(struct obj_def *od, u16_t len, void *value); + /** tests length and/or range BEFORE setting */ + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + /** sets object value, only to be called when set_test() */ + void (*set_value)(struct obj_def *od, u16_t len, void *value); + /** One out of MIB_NODE_AR, MIB_NODE_LR or MIB_NODE_EX */ + u8_t node_type; + /* array or max list length */ + u16_t maxlength; +}; + +/** derived node for scalars .0 index */ +typedef struct mib_node mib_scalar_node; + +/** derived node, points to a fixed size const array + of sub-identifiers plus a 'child' pointer */ +struct mib_array_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* additional struct members */ + const s32_t *objid; + struct mib_node* const *nptr; +}; + +/** derived node, points to a fixed size mem_malloced array + of sub-identifiers plus a 'child' pointer */ +struct mib_ram_array_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* aditional struct members */ + s32_t *objid; + struct mib_node **nptr; +}; + +struct mib_list_node +{ + struct mib_list_node *prev; + struct mib_list_node *next; + s32_t objid; + struct mib_node *nptr; +}; + +/** derived node, points to a doubly linked list + of sub-identifiers plus a 'child' pointer */ +struct mib_list_rootnode +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* additional struct members */ + struct mib_list_node *head; + struct mib_list_node *tail; + /* counts list nodes in list */ + u16_t count; +}; + +/** derived node, has access functions for mib object in external memory or device + using 'tree_level' and 'idx', with a range 0 .. (level_length() - 1) */ +struct mib_external_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* additional struct members */ + /** points to an external (in memory) record of some sort of addressing + information, passed to and interpreted by the funtions below */ + void* addr_inf; + /** tree levels under this node */ + u8_t tree_levels; + /** number of objects at this level */ + u16_t (*level_length)(void* addr_inf, u8_t level); + /** compares object sub identifier with external id + return zero when equal, nonzero when unequal */ + s32_t (*ident_cmp)(void* addr_inf, u8_t level, u16_t idx, s32_t sub_id); + void (*get_objid)(void* addr_inf, u8_t level, u16_t idx, s32_t *sub_id); + + /** async Questions */ + void (*get_object_def_q)(void* addr_inf, u8_t rid, u8_t ident_len, s32_t *ident); + void (*get_value_q)(u8_t rid, struct obj_def *od); + void (*set_test_q)(u8_t rid, struct obj_def *od); + void (*set_value_q)(u8_t rid, struct obj_def *od, u16_t len, void *value); + /** async Answers */ + void (*get_object_def_a)(u8_t rid, u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + u8_t (*set_test_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + void (*set_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + /** async Panic Close (agent returns error reply, + e.g. used for external transaction cleanup) */ + void (*get_object_def_pc)(u8_t rid, u8_t ident_len, s32_t *ident); + void (*get_value_pc)(u8_t rid, struct obj_def *od); + void (*set_test_pc)(u8_t rid, struct obj_def *od); + void (*set_value_pc)(u8_t rid, struct obj_def *od); +}; + +/** export MIB tree from mib2.c */ +extern const struct mib_array_node internet; + +/** dummy function pointers for non-leaf MIB nodes from mib2.c */ +void noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +void noleafs_get_value(struct obj_def *od, u16_t len, void *value); +u8_t noleafs_set_test(struct obj_def *od, u16_t len, void *value); +void noleafs_set_value(struct obj_def *od, u16_t len, void *value); + +void snmp_oidtoip(s32_t *ident, ip_addr_t *ip); +void snmp_iptooid(ip_addr_t *ip, s32_t *ident); +void snmp_ifindextonetif(s32_t ifindex, struct netif **netif); +void snmp_netiftoifindex(struct netif *netif, s32_t *ifidx); + +struct mib_list_node* snmp_mib_ln_alloc(s32_t id); +void snmp_mib_ln_free(struct mib_list_node *ln); +struct mib_list_rootnode* snmp_mib_lrn_alloc(void); +void snmp_mib_lrn_free(struct mib_list_rootnode *lrn); + +s8_t snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn); +s8_t snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn); +struct mib_list_rootnode *snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n); + +struct mib_node* snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np); +struct mib_node* snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret); +u8_t snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident); +u8_t snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_STRUCTS_H__ */ diff --git a/app/include/lwip/sockets.h b/app/include/lwip/sockets.h new file mode 100644 index 00000000..3c8fed24 --- /dev/null +++ b/app/include/lwip/sockets.h @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +#ifndef __LWIP_SOCKETS_H__ +#define __LWIP_SOCKETS_H__ + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + +#include "lwip/ip_addr.h" +#include "lwip/inet.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* members are in network byte order */ +struct sockaddr_in { + u8_t sin_len; + u8_t sin_family; + u16_t sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; + +struct sockaddr { + u8_t sa_len; + u8_t sa_family; + char sa_data[14]; +}; + +#ifndef socklen_t +# define socklen_t u32_t +#endif + +/* Socket protocol types (TCP/UDP/RAW) */ +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define SOCK_RAW 3 + +/* + * Option flags per-socket. These must match the SOF_ flags in ip.h (checked in init.c) + */ +#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_REUSEADDR 0x0004 /* Allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */ +#define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */ +#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */ + +#define SO_DONTLINGER ((int)(~SO_LINGER)) + +/* + * Additional options, not kept in so_options. + */ +#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */ +#define SO_RCVBUF 0x1002 /* receive buffer size */ +#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */ +#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */ +#define SO_SNDTIMEO 0x1005 /* Unimplemented: send timeout */ +#define SO_RCVTIMEO 0x1006 /* receive timeout */ +#define SO_ERROR 0x1007 /* get error status and clear */ +#define SO_TYPE 0x1008 /* get socket type */ +#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */ +#define SO_NO_CHECK 0x100a /* don't create UDP checksum */ + + +/* + * Structure used for manipulating linger option. + */ +struct linger { + int l_onoff; /* option on/off */ + int l_linger; /* linger time */ +}; + +/* + * Level number for (get/set)sockopt() to apply to socket itself. + */ +#define SOL_SOCKET 0xfff /* options for socket level */ + + +#define AF_UNSPEC 0 +#define AF_INET 2 +#define PF_INET AF_INET +#define PF_UNSPEC AF_UNSPEC + +#define IPPROTO_IP 0 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +#define IPPROTO_UDPLITE 136 + +/* Flags we can use with send and recv. */ +#define MSG_PEEK 0x01 /* Peeks at an incoming message */ +#define MSG_WAITALL 0x02 /* Unimplemented: Requests that the function block until the full amount of data requested can be returned */ +#define MSG_OOB 0x04 /* Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific */ +#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */ +#define MSG_MORE 0x10 /* Sender will send more */ + + +/* + * Options for level IPPROTO_IP + */ +#define IP_TOS 1 +#define IP_TTL 2 + +#if LWIP_TCP +/* + * Options for level IPPROTO_TCP + */ +#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ +#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */ +#define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */ +#define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */ +#define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */ +#endif /* LWIP_TCP */ + +#if LWIP_UDP && LWIP_UDPLITE +/* + * Options for level IPPROTO_UDPLITE + */ +#define UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */ +#define UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */ +#endif /* LWIP_UDP && LWIP_UDPLITE*/ + + +#if LWIP_IGMP +/* + * Options and types for UDP multicast traffic handling + */ +#define IP_ADD_MEMBERSHIP 3 +#define IP_DROP_MEMBERSHIP 4 +#define IP_MULTICAST_TTL 5 +#define IP_MULTICAST_IF 6 +#define IP_MULTICAST_LOOP 7 + +typedef struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +} ip_mreq; +#endif /* LWIP_IGMP */ + +/* + * The Type of Service provides an indication of the abstract + * parameters of the quality of service desired. These parameters are + * to be used to guide the selection of the actual service parameters + * when transmitting a datagram through a particular network. Several + * networks offer service precedence, which somehow treats high + * precedence traffic as more important than other traffic (generally + * by accepting only traffic above a certain precedence at time of high + * load). The major choice is a three way tradeoff between low-delay, + * high-reliability, and high-throughput. + * The use of the Delay, Throughput, and Reliability indications may + * increase the cost (in some sense) of the service. In many networks + * better performance for one of these parameters is coupled with worse + * performance on another. Except for very unusual cases at most two + * of these three indications should be set. + */ +#define IPTOS_TOS_MASK 0x1E +#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK) +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_LOWCOST 0x02 +#define IPTOS_MINCOST IPTOS_LOWCOST + +/* + * The Network Control precedence designation is intended to be used + * within a network only. The actual use and control of that + * designation is up to each network. The Internetwork Control + * designation is intended for use by gateway control originators only. + * If the actual use of these precedence designations is of concern to + * a particular network, it is the responsibility of that network to + * control the access to, and use of, those precedence designations. + */ +#define IPTOS_PREC_MASK 0xe0 +#define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK) +#define IPTOS_PREC_NETCONTROL 0xe0 +#define IPTOS_PREC_INTERNETCONTROL 0xc0 +#define IPTOS_PREC_CRITIC_ECP 0xa0 +#define IPTOS_PREC_FLASHOVERRIDE 0x80 +#define IPTOS_PREC_FLASH 0x60 +#define IPTOS_PREC_IMMEDIATE 0x40 +#define IPTOS_PREC_PRIORITY 0x20 +#define IPTOS_PREC_ROUTINE 0x00 + + +/* + * Commands for ioctlsocket(), taken from the BSD file fcntl.h. + * lwip_ioctl only supports FIONREAD and FIONBIO, for now + * + * Ioctl's have the command encoded in the lower word, + * and the size of any in or out parameters in the upper + * word. The high 2 bits of the upper word are used + * to encode the in/out status of the parameter; for now + * we restrict parameters to at most 128 bytes. + */ +#if !defined(FIONREAD) || !defined(FIONBIO) +#define IOCPARM_MASK 0x7fU /* parameters must be < 128 bytes */ +#define IOC_VOID 0x20000000UL /* no parameters */ +#define IOC_OUT 0x40000000UL /* copy out parameters */ +#define IOC_IN 0x80000000UL /* copy in parameters */ +#define IOC_INOUT (IOC_IN|IOC_OUT) + /* 0x20000000 distinguishes new & + old ioctl's */ +#define _IO(x,y) (IOC_VOID|((x)<<8)|(y)) + +#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) + +#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) +#endif /* !defined(FIONREAD) || !defined(FIONBIO) */ + +#ifndef FIONREAD +#define FIONREAD _IOR('f', 127, unsigned long) /* get # bytes to read */ +#endif +#ifndef FIONBIO +#define FIONBIO _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */ +#endif + +/* Socket I/O Controls: unimplemented */ +#ifndef SIOCSHIWAT +#define SIOCSHIWAT _IOW('s', 0, unsigned long) /* set high watermark */ +#define SIOCGHIWAT _IOR('s', 1, unsigned long) /* get high watermark */ +#define SIOCSLOWAT _IOW('s', 2, unsigned long) /* set low watermark */ +#define SIOCGLOWAT _IOR('s', 3, unsigned long) /* get low watermark */ +#define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */ +#endif + +/* commands for fnctl */ +#ifndef F_GETFL +#define F_GETFL 3 +#endif +#ifndef F_SETFL +#define F_SETFL 4 +#endif + +/* File status flags and file access modes for fnctl, + these are bits in an int. */ +#ifndef O_NONBLOCK +#define O_NONBLOCK 1 /* nonblocking I/O */ +#endif +#ifndef O_NDELAY +#define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */ +#endif + +#ifndef SHUT_RD + #define SHUT_RD 0 + #define SHUT_WR 1 + #define SHUT_RDWR 2 +#endif + +/* FD_SET used for lwip_select */ +#ifndef FD_SET + #undef FD_SETSIZE + /* Make FD_SETSIZE match NUM_SOCKETS in socket.c */ + #define FD_SETSIZE MEMP_NUM_NETCONN + #define FD_SET(n, p) ((p)->fd_bits[(n)/8] |= (1 << ((n) & 7))) + #define FD_CLR(n, p) ((p)->fd_bits[(n)/8] &= ~(1 << ((n) & 7))) + #define FD_ISSET(n,p) ((p)->fd_bits[(n)/8] & (1 << ((n) & 7))) + #define FD_ZERO(p) memset((void*)(p),0,sizeof(*(p))) + + typedef struct fd_set { + unsigned char fd_bits [(FD_SETSIZE+7)/8]; + } fd_set; + +#endif /* FD_SET */ + +/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided + * by your system, set this to 0 and include in cc.h */ +#ifndef LWIP_TIMEVAL_PRIVATE +#define LWIP_TIMEVAL_PRIVATE 1 +#endif + +#if LWIP_TIMEVAL_PRIVATE +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif /* LWIP_TIMEVAL_PRIVATE */ + +void lwip_socket_init(void); + +int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen); +int lwip_shutdown(int s, int how); +int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen); +int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen); +int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen); +int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen); +int lwip_close(int s); +int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen); +int lwip_listen(int s, int backlog); +int lwip_recv(int s, void *mem, size_t len, int flags); +int lwip_read(int s, void *mem, size_t len); +int lwip_recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen); +int lwip_send(int s, const void *dataptr, size_t size, int flags); +int lwip_sendto(int s, const void *dataptr, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen); +int lwip_socket(int domain, int type, int protocol); +int lwip_write(int s, const void *dataptr, size_t size); +int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout); +int lwip_ioctl(int s, long cmd, void *argp); +int lwip_fcntl(int s, int cmd, int val); + +#if LWIP_COMPAT_SOCKETS +#define accept(a,b,c) lwip_accept(a,b,c) +#define bind(a,b,c) lwip_bind(a,b,c) +#define shutdown(a,b) lwip_shutdown(a,b) +#define closesocket(s) lwip_close(s) +#define connect(a,b,c) lwip_connect(a,b,c) +#define getsockname(a,b,c) lwip_getsockname(a,b,c) +#define getpeername(a,b,c) lwip_getpeername(a,b,c) +#define setsockopt(a,b,c,d,e) lwip_setsockopt(a,b,c,d,e) +#define getsockopt(a,b,c,d,e) lwip_getsockopt(a,b,c,d,e) +#define listen(a,b) lwip_listen(a,b) +#define recv(a,b,c,d) lwip_recv(a,b,c,d) +#define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f) +#define send(a,b,c,d) lwip_send(a,b,c,d) +#define sendto(a,b,c,d,e,f) lwip_sendto(a,b,c,d,e,f) +#define socket(a,b,c) lwip_socket(a,b,c) +#define select(a,b,c,d,e) lwip_select(a,b,c,d,e) +#define ioctlsocket(a,b,c) lwip_ioctl(a,b,c) + +#if LWIP_POSIX_SOCKETS_IO_NAMES +#define read(a,b,c) lwip_read(a,b,c) +#define write(a,b,c) lwip_write(a,b,c) +#define close(s) lwip_close(s) +#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ + +#endif /* LWIP_COMPAT_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SOCKET */ + +#endif /* __LWIP_SOCKETS_H__ */ diff --git a/app/include/lwip/stats.h b/app/include/lwip/stats.h new file mode 100644 index 00000000..43883217 --- /dev/null +++ b/app/include/lwip/stats.h @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_STATS_H__ +#define __LWIP_STATS_H__ + +#include "lwip/opt.h" + +#include "lwip/mem.h" +#include "lwip/memp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_STATS + +#ifndef LWIP_STATS_LARGE +#define LWIP_STATS_LARGE 0 +#endif + +#if LWIP_STATS_LARGE +#define STAT_COUNTER u32_t +#define STAT_COUNTER_F U32_F +#else +#define STAT_COUNTER u16_t +#define STAT_COUNTER_F U16_F +#endif + +struct stats_proto { + STAT_COUNTER xmit; /* Transmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER fw; /* Forwarded packets. */ + STAT_COUNTER drop; /* Dropped packets. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER memerr; /* Out of memory error. */ + STAT_COUNTER rterr; /* Routing error. */ + STAT_COUNTER proterr; /* Protocol error. */ + STAT_COUNTER opterr; /* Error in options. */ + STAT_COUNTER err; /* Misc error. */ + STAT_COUNTER cachehit; +}; + +struct stats_igmp { + STAT_COUNTER xmit; /* Transmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER drop; /* Dropped packets. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER memerr; /* Out of memory error. */ + STAT_COUNTER proterr; /* Protocol error. */ + STAT_COUNTER rx_v1; /* Received v1 frames. */ + STAT_COUNTER rx_group; /* Received group-specific queries. */ + STAT_COUNTER rx_general; /* Received general queries. */ + STAT_COUNTER rx_report; /* Received reports. */ + STAT_COUNTER tx_join; /* Sent joins. */ + STAT_COUNTER tx_leave; /* Sent leaves. */ + STAT_COUNTER tx_report; /* Sent reports. */ +}; + +struct stats_mem { +#ifdef LWIP_DEBUG + const char *name; +#endif /* LWIP_DEBUG */ + mem_size_t avail; + mem_size_t used; + mem_size_t max; + STAT_COUNTER err; + STAT_COUNTER illegal; +}; + +struct stats_syselem { + STAT_COUNTER used; + STAT_COUNTER max; + STAT_COUNTER err; +}; + +struct stats_sys { + struct stats_syselem sem; + struct stats_syselem mutex; + struct stats_syselem mbox; +}; + +struct stats_ { +#if LINK_STATS + struct stats_proto link; +#endif +#if ETHARP_STATS + struct stats_proto etharp; +#endif +#if IPFRAG_STATS + struct stats_proto ip_frag; +#endif +#if IP_STATS + struct stats_proto ip; +#endif +#if ICMP_STATS + struct stats_proto icmp; +#endif +#if IGMP_STATS + struct stats_igmp igmp; +#endif +#if UDP_STATS + struct stats_proto udp; +#endif +#if TCP_STATS + struct stats_proto tcp; +#endif +#if MEM_STATS + struct stats_mem mem; +#endif +#if MEMP_STATS + struct stats_mem memp[MEMP_MAX]; +#endif +#if SYS_STATS + struct stats_sys sys; +#endif +}; + +extern struct stats_ lwip_stats; + +void stats_init(void)ICACHE_FLASH_ATTR; + +#define STATS_INC(x) ++lwip_stats.x +#define STATS_DEC(x) --lwip_stats.x +#define STATS_INC_USED(x, y) do { lwip_stats.x.used += y; \ + if (lwip_stats.x.max < lwip_stats.x.used) { \ + lwip_stats.x.max = lwip_stats.x.used; \ + } \ + } while(0) +#else /* LWIP_STATS */ +#define stats_init() +#define STATS_INC(x) +#define STATS_DEC(x) +#define STATS_INC_USED(x) +#endif /* LWIP_STATS */ + +#if TCP_STATS +#define TCP_STATS_INC(x) STATS_INC(x) +#define TCP_STATS_DISPLAY() stats_display_proto(&lwip_stats.tcp, "TCP") +#else +#define TCP_STATS_INC(x) +#define TCP_STATS_DISPLAY() +#endif + +#if UDP_STATS +#define UDP_STATS_INC(x) STATS_INC(x) +#define UDP_STATS_DISPLAY() stats_display_proto(&lwip_stats.udp, "UDP") +#else +#define UDP_STATS_INC(x) +#define UDP_STATS_DISPLAY() +#endif + +#if ICMP_STATS +#define ICMP_STATS_INC(x) STATS_INC(x) +#define ICMP_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp, "ICMP") +#else +#define ICMP_STATS_INC(x) +#define ICMP_STATS_DISPLAY() +#endif + +#if IGMP_STATS +#define IGMP_STATS_INC(x) STATS_INC(x) +#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp) +#else +#define IGMP_STATS_INC(x) +#define IGMP_STATS_DISPLAY() +#endif + +#if IP_STATS +#define IP_STATS_INC(x) STATS_INC(x) +#define IP_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip, "IP") +#else +#define IP_STATS_INC(x) +#define IP_STATS_DISPLAY() +#endif + +#if IPFRAG_STATS +#define IPFRAG_STATS_INC(x) STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG") +#else +#define IPFRAG_STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() +#endif + +#if ETHARP_STATS +#define ETHARP_STATS_INC(x) STATS_INC(x) +#define ETHARP_STATS_DISPLAY() stats_display_proto(&lwip_stats.etharp, "ETHARP") +#else +#define ETHARP_STATS_INC(x) +#define ETHARP_STATS_DISPLAY() +#endif + +#if LINK_STATS +#define LINK_STATS_INC(x) STATS_INC(x) +#define LINK_STATS_DISPLAY() stats_display_proto(&lwip_stats.link, "LINK") +#else +#define LINK_STATS_INC(x) +#define LINK_STATS_DISPLAY() +#endif + +#if MEM_STATS +#define MEM_STATS_AVAIL(x, y) lwip_stats.mem.x = y +#define MEM_STATS_INC(x) STATS_INC(mem.x) +#define MEM_STATS_INC_USED(x, y) STATS_INC_USED(mem, y) +#define MEM_STATS_DEC_USED(x, y) lwip_stats.mem.x -= y +#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP") +#else +#define MEM_STATS_AVAIL(x, y) +#define MEM_STATS_INC(x) +#define MEM_STATS_INC_USED(x, y) +#define MEM_STATS_DEC_USED(x, y) +#define MEM_STATS_DISPLAY() +#endif + +#if MEMP_STATS +#define MEMP_STATS_AVAIL(x, i, y) lwip_stats.memp[i].x = y +#define MEMP_STATS_INC(x, i) STATS_INC(memp[i].x) +#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i].x) +#define MEMP_STATS_INC_USED(x, i) STATS_INC_USED(memp[i], 1) +#define MEMP_STATS_DISPLAY(i) stats_display_memp(&lwip_stats.memp[i], i) +#else +#define MEMP_STATS_AVAIL(x, i, y) +#define MEMP_STATS_INC(x, i) +#define MEMP_STATS_DEC(x, i) +#define MEMP_STATS_INC_USED(x, i) +#define MEMP_STATS_DISPLAY(i) +#endif + +#if SYS_STATS +#define SYS_STATS_INC(x) STATS_INC(sys.x) +#define SYS_STATS_DEC(x) STATS_DEC(sys.x) +#define SYS_STATS_INC_USED(x) STATS_INC_USED(sys.x, 1) +#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys) +#else +#define SYS_STATS_INC(x) +#define SYS_STATS_DEC(x) +#define SYS_STATS_INC_USED(x) +#define SYS_STATS_DISPLAY() +#endif + +/* Display of statistics */ +#if LWIP_STATS_DISPLAY +void stats_display(void)ICACHE_FLASH_ATTR; +void stats_display_proto(struct stats_proto *proto, char *name)ICACHE_FLASH_ATTR; +void stats_display_igmp(struct stats_igmp *igmp)ICACHE_FLASH_ATTR; +void stats_display_mem(struct stats_mem *mem, char *name)ICACHE_FLASH_ATTR; +void stats_display_memp(struct stats_mem *mem, int index)ICACHE_FLASH_ATTR; +void stats_display_sys(struct stats_sys *sys)ICACHE_FLASH_ATTR; +#else /* LWIP_STATS_DISPLAY */ +#define stats_display() +#define stats_display_proto(proto, name) +#define stats_display_igmp(igmp) +#define stats_display_mem(mem, name) +#define stats_display_memp(mem, index) +#define stats_display_sys(sys) +#endif /* LWIP_STATS_DISPLAY */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_STATS_H__ */ diff --git a/app/include/lwip/sys.h b/app/include/lwip/sys.h new file mode 100644 index 00000000..31a9dea7 --- /dev/null +++ b/app/include/lwip/sys.h @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_SYS_H__ +#define __LWIP_SYS_H__ + +#include "lwip/opt.h" + +#include "eagle_soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if NO_SYS + +/* For a totally minimal and standalone system, we provide null + definitions of the sys_ functions. */ +typedef u8_t sys_sem_t; +typedef u8_t sys_mutex_t; +typedef u8_t sys_mbox_t; + +#define sys_sem_new(s, c) ERR_OK +#define sys_sem_signal(s) +#define sys_sem_wait(s) +#define sys_arch_sem_wait(s,t) +#define sys_sem_free(s) +#define sys_mutex_new(mu) ERR_OK +#define sys_mutex_lock(mu) +#define sys_mutex_unlock(mu) +#define sys_mutex_free(mu) +#define sys_mbox_new(m, s) ERR_OK +#define sys_mbox_fetch(m,d) +#define sys_mbox_tryfetch(m,d) +#define sys_mbox_post(m,d) +#define sys_mbox_trypost(m,d) +#define sys_mbox_free(m) + +#define sys_thread_new(n,t,a,s,p) + +#define sys_msleep(t) + +#else /* NO_SYS */ + +/** Return code for timeouts from sys_arch_mbox_fetch and sys_arch_sem_wait */ +#define SYS_ARCH_TIMEOUT 0xffffffffUL + +/** sys_mbox_tryfetch() returns SYS_MBOX_EMPTY if appropriate. + * For now we use the same magic value, but we allow this to change in future. + */ +#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT + +#include "lwip/err.h" +#include "arch/sys_arch.h" + +/** Function prototype for thread functions */ +typedef void (*lwip_thread_fn)(void *arg); + +/* Function prototypes for functions to be implemented by platform ports + (in sys_arch.c) */ + +/* Mutex functions: */ + +/** Define LWIP_COMPAT_MUTEX if the port has no mutexes and binary semaphores + should be used instead */ +#if LWIP_COMPAT_MUTEX +/* for old ports that don't have mutexes: define them to binary semaphores */ +#define sys_mutex_t sys_sem_t +#define sys_mutex_new(mutex) sys_sem_new(mutex, 1) +#define sys_mutex_lock(mutex) sys_sem_wait(mutex) +#define sys_mutex_unlock(mutex) sys_sem_signal(mutex) +#define sys_mutex_free(mutex) sys_sem_free(mutex) +#define sys_mutex_valid(mutex) sys_sem_valid(mutex) +#define sys_mutex_set_invalid(mutex) sys_sem_set_invalid(mutex) + +#else /* LWIP_COMPAT_MUTEX */ + +/** Create a new mutex + * @param mutex pointer to the mutex to create + * @return a new mutex */ +err_t sys_mutex_new(sys_mutex_t *mutex); +/** Lock a mutex + * @param mutex the mutex to lock */ +void sys_mutex_lock(sys_mutex_t *mutex); +/** Unlock a mutex + * @param mutex the mutex to unlock */ +void sys_mutex_unlock(sys_mutex_t *mutex); +/** Delete a semaphore + * @param mutex the mutex to delete */ +void sys_mutex_free(sys_mutex_t *mutex); +#ifndef sys_mutex_valid +/** Check if a mutex is valid/allocated: return 1 for valid, 0 for invalid */ +int sys_mutex_valid(sys_mutex_t *mutex); +#endif +#ifndef sys_mutex_set_invalid +/** Set a mutex invalid so that sys_mutex_valid returns 0 */ +void sys_mutex_set_invalid(sys_mutex_t *mutex); +#endif +#endif /* LWIP_COMPAT_MUTEX */ + +/* Semaphore functions: */ + +/** Create a new semaphore + * @param sem pointer to the semaphore to create + * @param count initial count of the semaphore + * @return ERR_OK if successful, another err_t otherwise */ +err_t sys_sem_new(sys_sem_t *sem, u8_t count); +/** Signals a semaphore + * @param sem the semaphore to signal */ +void sys_sem_signal(sys_sem_t *sem); +/** Wait for a semaphore for the specified timeout + * @param sem the semaphore to wait for + * @param timeout timeout in milliseconds to wait (0 = wait forever) + * @return time (in milliseconds) waited for the semaphore + * or SYS_ARCH_TIMEOUT on timeout */ +u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout); +/** Delete a semaphore + * @param sem semaphore to delete */ +void sys_sem_free(sys_sem_t *sem); +/** Wait for a semaphore - forever/no timeout */ +#define sys_sem_wait(sem) sys_arch_sem_wait(sem, 0) +#ifndef sys_sem_valid +/** Check if a sempahore is valid/allocated: return 1 for valid, 0 for invalid */ +int sys_sem_valid(sys_sem_t *sem); +#endif +#ifndef sys_sem_set_invalid +/** Set a semaphore invalid so that sys_sem_valid returns 0 */ +void sys_sem_set_invalid(sys_sem_t *sem); +#endif + +/* Time functions. */ +#ifndef sys_msleep +void sys_msleep(u32_t ms); /* only has a (close to) 1 jiffy resolution. */ +#endif + +/* Mailbox functions. */ + +/** Create a new mbox of specified size + * @param mbox pointer to the mbox to create + * @param size (miminum) number of messages in this mbox + * @return ERR_OK if successful, another err_t otherwise */ +err_t sys_mbox_new(sys_mbox_t *mbox, int size); +/** Post a message to an mbox - may not fail + * -> blocks if full, only used from tasks not from ISR + * @param mbox mbox to posts the message + * @param msg message to post (ATTENTION: can be NULL) */ +void sys_mbox_post(sys_mbox_t *mbox, void *msg); +/** Try to post a message to an mbox - may fail if full or ISR + * @param mbox mbox to posts the message + * @param msg message to post (ATTENTION: can be NULL) */ +err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg); +/** Wait for a new message to arrive in the mbox + * @param mbox mbox to get a message from + * @param msg pointer where the message is stored + * @param timeout maximum time (in milliseconds) to wait for a message + * @return time (in milliseconds) waited for a message, may be 0 if not waited + or SYS_ARCH_TIMEOUT on timeout + * The returned time has to be accurate to prevent timer jitter! */ +u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout); +/* Allow port to override with a macro, e.g. special timout for sys_arch_mbox_fetch() */ +#ifndef sys_arch_mbox_tryfetch +/** Wait for a new message to arrive in the mbox + * @param mbox mbox to get a message from + * @param msg pointer where the message is stored + * @param timeout maximum time (in milliseconds) to wait for a message + * @return 0 (milliseconds) if a message has been received + * or SYS_MBOX_EMPTY if the mailbox is empty */ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg); +#endif +/** For now, we map straight to sys_arch implementation. */ +#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg) +/** Delete an mbox + * @param mbox mbox to delete */ +void sys_mbox_free(sys_mbox_t *mbox); +#define sys_mbox_fetch(mbox, msg) sys_arch_mbox_fetch(mbox, msg, 0) +#ifndef sys_mbox_valid +/** Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid */ +int sys_mbox_valid(sys_mbox_t *mbox); +#endif +#ifndef sys_mbox_set_invalid +/** Set an mbox invalid so that sys_mbox_valid returns 0 */ +void sys_mbox_set_invalid(sys_mbox_t *mbox); +#endif + +/** The only thread function: + * Creates a new thread + * @param name human-readable name for the thread (used for debugging purposes) + * @param thread thread-function + * @param arg parameter passed to 'thread' + * @param stacksize stack size in bytes for the new thread (may be ignored by ports) + * @param prio priority of the new thread (may be ignored by ports) */ +sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio); + +#endif /* NO_SYS */ + +/* sys_init() must be called before anthing else. */ +void sys_init(void)ICACHE_FLASH_ATTR; + +#ifndef sys_jiffies +/** Ticks/jiffies since power up. */ +u32_t sys_jiffies(void)ICACHE_FLASH_ATTR; +#endif + +/** Returns the current time in milliseconds, + * may be the same as sys_jiffies or at least based on it. */ +static inline u32_t sys_now(void) ICACHE_FLASH_ATTR; +static inline u32_t sys_now(void) +{ + return NOW()/(TIMER_CLK_FREQ/1000); +} + +/* Critical Region Protection */ +/* These functions must be implemented in the sys_arch.c file. + In some implementations they can provide a more light-weight protection + mechanism than using semaphores. Otherwise semaphores can be used for + implementation */ +#ifndef SYS_ARCH_PROTECT +/** SYS_LIGHTWEIGHT_PROT + * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection + * for certain critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#if SYS_LIGHTWEIGHT_PROT + +/** SYS_ARCH_DECL_PROTECT + * declare a protection variable. This macro will default to defining a variable of + * type sys_prot_t. If a particular port needs a different implementation, then + * this macro may be defined in sys_arch.h. + */ +#define SYS_ARCH_DECL_PROTECT(lev) sys_prot_t lev +/** SYS_ARCH_PROTECT + * Perform a "fast" protect. This could be implemented by + * disabling interrupts for an embedded system or by using a semaphore or + * mutex. The implementation should allow calling SYS_ARCH_PROTECT when + * already protected. The old protection level is returned in the variable + * "lev". This macro will default to calling the sys_arch_protect() function + * which should be implemented in sys_arch.c. If a particular port needs a + * different implementation, then this macro may be defined in sys_arch.h + */ +#define SYS_ARCH_PROTECT(lev) lev = sys_arch_protect() +/** SYS_ARCH_UNPROTECT + * Perform a "fast" set of the protection level to "lev". This could be + * implemented by setting the interrupt level to "lev" within the MACRO or by + * using a semaphore or mutex. This macro will default to calling the + * sys_arch_unprotect() function which should be implemented in + * sys_arch.c. If a particular port needs a different implementation, then + * this macro may be defined in sys_arch.h + */ +#define SYS_ARCH_UNPROTECT(lev) sys_arch_unprotect(lev) +sys_prot_t sys_arch_protect(void)ICACHE_FLASH_ATTR; +void sys_arch_unprotect(sys_prot_t pval)ICACHE_FLASH_ATTR; + +#else + +#define SYS_ARCH_DECL_PROTECT(lev) +#define SYS_ARCH_PROTECT(lev) lev = os_intr_lock() //fix by ives at 2014.3.24 +#define SYS_ARCH_UNPROTECT(lev) lev = os_intr_unlock() + +#endif /* SYS_LIGHTWEIGHT_PROT */ + +#endif /* SYS_ARCH_PROTECT */ + +/* + * Macros to set/get and increase/decrease variables in a thread-safe way. + * Use these for accessing variable that are used from more than one thread. + */ + +#ifndef SYS_ARCH_INC +#define SYS_ARCH_INC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var += val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_INC */ + +#ifndef SYS_ARCH_DEC +#define SYS_ARCH_DEC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var -= val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_DEC */ + +#ifndef SYS_ARCH_GET +#define SYS_ARCH_GET(var, ret) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + ret = var; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_GET */ + +#ifndef SYS_ARCH_SET +#define SYS_ARCH_SET(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var = val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_SET */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_SYS_H__ */ diff --git a/app/include/lwip/tcp.h b/app/include/lwip/tcp.h new file mode 100644 index 00000000..909ff9b7 --- /dev/null +++ b/app/include/lwip/tcp.h @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCP_H__ +#define __LWIP_TCP_H__ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct tcp_pcb; + +/** Function prototype for tcp accept callback functions. Called when a new + * connection can be accepted on a listening pcb. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param newpcb The new connection pcb + * @param err An error code if there has been an error accepting. + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, err_t err); + +/** Function prototype for tcp receive callback functions. Called when data has + * been received. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb which received data + * @param p The received data (or NULL when the connection has been closed!) + * @param err An error code if there has been an error receiving + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_recv_fn)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err); + +/** Function prototype for tcp sent callback functions. Called when sent data has + * been acknowledged by the remote side. Use it to free corresponding resources. + * This also means that the pcb has now space available to send new data. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb for which data has been acknowledged + * @param len The amount of bytes acknowledged + * @return ERR_OK: try to send some data by calling tcp_output + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_sent_fn)(void *arg, struct tcp_pcb *tpcb, + u16_t len); + +/** Function prototype for tcp poll callback functions. Called periodically as + * specified by @see tcp_poll. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb tcp pcb + * @return ERR_OK: try to send some data by calling tcp_output + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_poll_fn)(void *arg, struct tcp_pcb *tpcb); + +/** Function prototype for tcp error callback functions. Called when the pcb + * receives a RST or is unexpectedly closed for any other reason. + * + * @note The corresponding pcb is already freed when this callback is called! + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param err Error code to indicate why the pcb has been closed + * ERR_ABRT: aborted through tcp_abort or by a TCP timer + * ERR_RST: the connection was reset by the remote host + */ +typedef void (*tcp_err_fn)(void *arg, err_t err); + +/** Function prototype for tcp connected callback functions. Called when a pcb + * is connected to the remote side after initiating a connection attempt by + * calling tcp_connect(). + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb which is connected + * @param err An unused error code, always ERR_OK currently ;-) TODO! + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + * + * @note When a connection attempt fails, the error callback is currently called! + */ +typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err); + +enum tcp_state { + CLOSED = 0, + LISTEN = 1, + SYN_SENT = 2, + SYN_RCVD = 3, + ESTABLISHED = 4, + FIN_WAIT_1 = 5, + FIN_WAIT_2 = 6, + CLOSE_WAIT = 7, + CLOSING = 8, + LAST_ACK = 9, + TIME_WAIT = 10 +}; + +#if LWIP_CALLBACK_API + /* Function to call when a listener has been connected. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb a new tcp_pcb that now is connected + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return ERR_OK: accept the new connection, + * any other err_t abortsthe new connection + */ +#define DEF_ACCEPT_CALLBACK tcp_accept_fn accept; +#else /* LWIP_CALLBACK_API */ +#define DEF_ACCEPT_CALLBACK +#endif /* LWIP_CALLBACK_API */ + +/** + * members common to struct tcp_pcb and struct tcp_listen_pcb + */ +#define TCP_PCB_COMMON(type) \ + type *next; /* for the linked list */ \ + enum tcp_state state; /* TCP state */ \ + u8_t prio; \ + void *callback_arg; \ + /* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \ + DEF_ACCEPT_CALLBACK \ + /* ports are in host byte order */ \ + u16_t local_port + + +/* the TCP protocol control block */ +struct tcp_pcb { +/** common PCB members */ + IP_PCB; +/** protocol specific PCB members */ + TCP_PCB_COMMON(struct tcp_pcb); + + /* ports are in host byte order */ + u16_t remote_port; + + u8_t flags; +#define TF_ACK_DELAY ((u8_t)0x01U) /* Delayed ACK. */ +#define TF_ACK_NOW ((u8_t)0x02U) /* Immediate ACK. */ +#define TF_INFR ((u8_t)0x04U) /* In fast recovery. */ +#define TF_TIMESTAMP ((u8_t)0x08U) /* Timestamp option enabled */ +#define TF_RXCLOSED ((u8_t)0x10U) /* rx closed by tcp_shutdown */ +#define TF_FIN ((u8_t)0x20U) /* Connection was closed locally (FIN segment enqueued). */ +#define TF_NODELAY ((u8_t)0x40U) /* Disable Nagle algorithm */ +#define TF_NAGLEMEMERR ((u8_t)0x80U) /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */ + + /* the rest of the fields are in host byte order + as we have to do some math with them */ + /* receiver variables */ + u32_t rcv_nxt; /* next seqno expected */ + u16_t rcv_wnd; /* receiver window available */ + u16_t rcv_ann_wnd; /* receiver window to announce */ + u32_t rcv_ann_right_edge; /* announced right edge of window */ + + /* Timers */ + u32_t tmr; + u8_t polltmr, pollinterval; + + /* Retransmission timer. */ + s16_t rtime; + + u16_t mss; /* maximum segment size */ + + /* RTT (round trip time) estimation variables */ + u32_t rttest; /* RTT estimate in 500ms ticks */ + u32_t rtseq; /* sequence number being timed */ + s16_t sa, sv; /* @todo document this */ + + s16_t rto; /* retransmission time-out */ + u8_t nrtx; /* number of retransmissions */ + + /* fast retransmit/recovery */ + u32_t lastack; /* Highest acknowledged seqno. */ + u8_t dupacks; + + /* congestion avoidance/control variables */ + u16_t cwnd; + u16_t ssthresh; + + /* sender variables */ + u32_t snd_nxt; /* next new seqno to be sent */ + u16_t snd_wnd; /* sender window */ + u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last + window update. */ + u32_t snd_lbb; /* Sequence number of next byte to be buffered. */ + + u16_t acked; + + u16_t snd_buf; /* Available buffer space for sending (in bytes). */ +#define TCP_SNDQUEUELEN_OVERFLOW (0xffff-3) + u16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */ + +#if TCP_OVERSIZE + /* Extra bytes available at the end of the last pbuf in unsent. */ + u16_t unsent_oversize; +#endif /* TCP_OVERSIZE */ + + /* These are ordered by sequence number: */ + struct tcp_seg *unsent; /* Unsent (queued) segments. */ + struct tcp_seg *unacked; /* Sent but unacknowledged segments. */ +#if TCP_QUEUE_OOSEQ + struct tcp_seg *ooseq; /* Received out of sequence segments. */ +#endif /* TCP_QUEUE_OOSEQ */ + + struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */ + +#if LWIP_CALLBACK_API + /* Function to be called when more send buffer space is available. */ + tcp_sent_fn sent; + /* Function to be called when (in-sequence) data has arrived. */ + tcp_recv_fn recv; + /* Function to be called when a connection has been set up. */ + tcp_connected_fn connected; + /* Function which is called periodically. */ + tcp_poll_fn poll; + /* Function to be called whenever a fatal error occurs. */ + tcp_err_fn errf; +#endif /* LWIP_CALLBACK_API */ + +#if LWIP_TCP_TIMESTAMPS + u32_t ts_lastacksent; + u32_t ts_recent; +#endif /* LWIP_TCP_TIMESTAMPS */ + + /* idle time before KEEPALIVE is sent */ + u32_t keep_idle; +#if LWIP_TCP_KEEPALIVE + u32_t keep_intvl; + u32_t keep_cnt; +#endif /* LWIP_TCP_KEEPALIVE */ + + /* Persist timer counter */ + u32_t persist_cnt; + /* Persist timer back-off */ + u8_t persist_backoff; + + /* KEEPALIVE counter */ + u8_t keep_cnt_sent; +}; + +struct tcp_pcb_listen { +/* Common members of all PCB types */ + IP_PCB; +/* Protocol specific PCB members */ + TCP_PCB_COMMON(struct tcp_pcb_listen); + +#if TCP_LISTEN_BACKLOG + u8_t backlog; + u8_t accepts_pending; +#endif /* TCP_LISTEN_BACKLOG */ +}; + +#if LWIP_EVENT_API + +enum lwip_event { + LWIP_EVENT_ACCEPT, + LWIP_EVENT_SENT, + LWIP_EVENT_RECV, + LWIP_EVENT_CONNECTED, + LWIP_EVENT_POLL, + LWIP_EVENT_ERR +}; + +err_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb, + enum lwip_event, + struct pbuf *p, + u16_t size, + err_t err); + +#endif /* LWIP_EVENT_API */ + +/* Application program's interface: */ +struct tcp_pcb * tcp_new (void)ICACHE_FLASH_ATTR; + +void tcp_arg (struct tcp_pcb *pcb, void *arg) ICACHE_FLASH_ATTR; +void tcp_accept (struct tcp_pcb *pcb, tcp_accept_fn accept) ICACHE_FLASH_ATTR; +void tcp_recv (struct tcp_pcb *pcb, tcp_recv_fn recv) ICACHE_FLASH_ATTR; +void tcp_sent (struct tcp_pcb *pcb, tcp_sent_fn sent)ICACHE_FLASH_ATTR; +void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval)ICACHE_FLASH_ATTR; +void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err)ICACHE_FLASH_ATTR; + +#define tcp_mss(pcb) (((pcb)->flags & TF_TIMESTAMP) ? ((pcb)->mss - 12) : (pcb)->mss) +#define tcp_sndbuf(pcb) ((pcb)->snd_buf) +#define tcp_sndqueuelen(pcb) ((pcb)->snd_queuelen) +#define tcp_nagle_disable(pcb) ((pcb)->flags |= TF_NODELAY) +#define tcp_nagle_enable(pcb) ((pcb)->flags &= ~TF_NODELAY) +#define tcp_nagle_disabled(pcb) (((pcb)->flags & TF_NODELAY) != 0) + +#if TCP_LISTEN_BACKLOG +#define tcp_accepted(pcb) do { \ + LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", pcb->state == LISTEN); \ + (((struct tcp_pcb_listen *)(pcb))->accepts_pending--); } while(0) +#else /* TCP_LISTEN_BACKLOG */ +#define tcp_accepted(pcb) LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", \ + pcb->state == LISTEN) +#endif /* TCP_LISTEN_BACKLOG */ + +void tcp_recved (struct tcp_pcb *pcb, u16_t len)ICACHE_FLASH_ATTR; +err_t tcp_bind (struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port)ICACHE_FLASH_ATTR; +err_t tcp_connect (struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port, tcp_connected_fn connected)ICACHE_FLASH_ATTR; + +struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)ICACHE_FLASH_ATTR; +#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) + +void tcp_abort (struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +err_t tcp_close (struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +err_t tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)ICACHE_FLASH_ATTR; + +/* Flags for "apiflags" parameter in tcp_write */ +#define TCP_WRITE_FLAG_COPY 0x01 +#define TCP_WRITE_FLAG_MORE 0x02 + +err_t tcp_write (struct tcp_pcb *pcb, const void *dataptr, u16_t len, + u8_t apiflags)ICACHE_FLASH_ATTR; + +void tcp_setprio (struct tcp_pcb *pcb, u8_t prio)ICACHE_FLASH_ATTR; + +#define TCP_PRIO_MIN 1 +#define TCP_PRIO_NORMAL 64 +#define TCP_PRIO_MAX 127 + +extern err_t tcp_output(struct tcp_pcb *pcb); + + +const char* tcp_debug_state_str(enum tcp_state s)ICACHE_FLASH_ATTR; + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* __LWIP_TCP_H__ */ diff --git a/app/include/lwip/tcp_impl.h b/app/include/lwip/tcp_impl.h new file mode 100644 index 00000000..a756781a --- /dev/null +++ b/app/include/lwip/tcp_impl.h @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCP_IMPL_H__ +#define __LWIP_TCP_IMPL_H__ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Functions for interfacing with TCP: */ + +/* Lower layer interface to TCP: */ +#define tcp_init() /* Compatibility define, no init needed. */ +void tcp_tmr (void)ICACHE_FLASH_ATTR; /* Must be called every + TCP_TMR_INTERVAL + ms. (Typically 250 ms). */ +/* It is also possible to call these two functions at the right + intervals (instead of calling tcp_tmr()). */ +void tcp_slowtmr (void)ICACHE_FLASH_ATTR; +void tcp_fasttmr (void)ICACHE_FLASH_ATTR; + + +/* Only used by IP to pass a TCP segment to TCP: */ +void tcp_input (struct pbuf *p, struct netif *inp)ICACHE_FLASH_ATTR; +/* Used within the TCP code only: */ +struct tcp_pcb * tcp_alloc (u8_t prio)ICACHE_FLASH_ATTR; +void tcp_abandon (struct tcp_pcb *pcb, int reset)ICACHE_FLASH_ATTR; +err_t tcp_send_empty_ack(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +void tcp_rexmit (struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +void tcp_rexmit_rto (struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +void tcp_rexmit_fast (struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; + +/** + * This is the Nagle algorithm: try to combine user data to send as few TCP + * segments as possible. Only send if + * - no previously transmitted data on the connection remains unacknowledged or + * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or + * - the only unsent segment is at least pcb->mss bytes long (or there is more + * than one unsent segment - with lwIP, this can happen although unsent->len < mss) + * - or if we are in fast-retransmit (TF_INFR) + */ +#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \ + ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \ + (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \ + ((tpcb)->unsent->len >= (tpcb)->mss))) \ + ) ? 1 : 0) +#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK) + + +#define TCP_SEQ_LT(a,b) ((s32_t)((a)-(b)) < 0) +#define TCP_SEQ_LEQ(a,b) ((s32_t)((a)-(b)) <= 0) +#define TCP_SEQ_GT(a,b) ((s32_t)((a)-(b)) > 0) +#define TCP_SEQ_GEQ(a,b) ((s32_t)((a)-(b)) >= 0) +/* is b<=a<=c? */ +#if 0 /* see bug #10548 */ +#define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b)) +#endif +#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c)) +#define TCP_FIN 0x01U +#define TCP_SYN 0x02U +#define TCP_RST 0x04U +#define TCP_PSH 0x08U +#define TCP_ACK 0x10U +#define TCP_URG 0x20U +#define TCP_ECE 0x40U +#define TCP_CWR 0x80U + +#define TCP_FLAGS 0x3fU + +/* Length of the TCP header, excluding options. */ +#define TCP_HLEN 20 + +#ifndef TCP_TMR_INTERVAL +#define TCP_TMR_INTERVAL 125 /* The TCP timer interval in milliseconds. */ +#endif /* TCP_TMR_INTERVAL */ + +#ifndef TCP_FAST_INTERVAL +#define TCP_FAST_INTERVAL TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */ +#endif /* TCP_FAST_INTERVAL */ + +#ifndef TCP_SLOW_INTERVAL +#define TCP_SLOW_INTERVAL (2*TCP_TMR_INTERVAL) /* the coarse grained timeout in milliseconds */ +#endif /* TCP_SLOW_INTERVAL */ + +#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */ +#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */ + +#define TCP_OOSEQ_TIMEOUT 6U /* x RTO */ + +#ifndef TCP_MSL +#define TCP_MSL 60000UL /* The maximum segment lifetime in milliseconds */ +#endif + +/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */ +#ifndef TCP_KEEPIDLE_DEFAULT +#define TCP_KEEPIDLE_DEFAULT 3000UL /* Default KEEPALIVE timer in milliseconds */ +#endif + +#ifndef TCP_KEEPINTVL_DEFAULT +#define TCP_KEEPINTVL_DEFAULT 1000UL /* Default Time between KEEPALIVE probes in milliseconds */ +#endif + +#ifndef TCP_KEEPCNT_DEFAULT +#define TCP_KEEPCNT_DEFAULT 3U /* Default Counter for KEEPALIVE probes */ +#endif + +#define TCP_MAXIDLE TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT /* Maximum KEEPALIVE probe time */ + +/* Fields are (of course) in network byte order. + * Some fields are converted to host byte order in tcp_input(). + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct tcp_hdr { + PACK_STRUCT_FIELD(u16_t src); //Դ�˿� + PACK_STRUCT_FIELD(u16_t dest); //Ŀ�Ķ˿� + PACK_STRUCT_FIELD(u32_t seqno); //��� + PACK_STRUCT_FIELD(u32_t ackno); //Ӧ����� + PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags);//�ײ�����+����λ+��־λ + PACK_STRUCT_FIELD(u16_t wnd); //���ڴ�С + PACK_STRUCT_FIELD(u16_t chksum); //У��� + PACK_STRUCT_FIELD(u16_t urgp); //����ָ�� +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define TCPH_OFFSET(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 8) +#define TCPH_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12) +#define TCPH_FLAGS(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS) + +#define TCPH_OFFSET_SET(phdr, offset) (phdr)->_hdrlen_rsvd_flags = htons(((offset) << 8) | TCPH_FLAGS(phdr)) +#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr)) +#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS((u16_t)(~(u16_t)(TCP_FLAGS)))) | htons(flags)) +#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | (flags)) + +#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | htons(flags)) +#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags) | (TCPH_FLAGS(phdr) & ~(flags)) ) + +#define TCP_TCPLEN(seg) ((seg)->len + ((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0)) + +/** Flags used on input processing, not on pcb->flags +*/ +#define TF_RESET (u8_t)0x08U /* Connection was reset. */ +#define TF_CLOSED (u8_t)0x10U /* Connection was sucessfully closed. */ +#define TF_GOT_FIN (u8_t)0x20U /* Connection was closed by the remote end. */ + + +#if LWIP_EVENT_API + +#define TCP_EVENT_ACCEPT(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_ACCEPT, NULL, 0, err) +#define TCP_EVENT_SENT(pcb,space,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_SENT, NULL, space, ERR_OK) +#define TCP_EVENT_RECV(pcb,p,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_RECV, (p), 0, (err)) +#define TCP_EVENT_CLOSED(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_RECV, NULL, 0, ERR_OK) +#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_CONNECTED, NULL, 0, (err)) +#define TCP_EVENT_POLL(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_POLL, NULL, 0, ERR_OK) +#define TCP_EVENT_ERR(errf,arg,err) lwip_tcp_event((arg), NULL, \ + LWIP_EVENT_ERR, NULL, 0, (err)) + +#else /* LWIP_EVENT_API */ + +#define TCP_EVENT_ACCEPT(pcb,err,ret) \ + do { \ + if((pcb)->accept != NULL) \ + (ret) = (pcb)->accept((pcb)->callback_arg,(pcb),(err)); \ + else (ret) = ERR_ARG; \ + } while (0) + +#define TCP_EVENT_SENT(pcb,space,ret) \ + do { \ + if((pcb)->sent != NULL) \ + (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_RECV(pcb,p,err,ret) \ + do { \ + if((pcb)->recv != NULL) { \ + (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\ + } else { \ + (ret) = tcp_recv_null(NULL, (pcb), (p), (err)); \ + } \ + } while (0) + +#define TCP_EVENT_CLOSED(pcb,ret) \ + do { \ + if(((pcb)->recv != NULL)) { \ + (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,ERR_OK);\ + } else { \ + (ret) = ERR_OK; \ + } \ + } while (0) + +#define TCP_EVENT_CONNECTED(pcb,err,ret) \ + do { \ + if((pcb)->connected != NULL) \ + (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_POLL(pcb,ret) \ + do { \ + if((pcb)->poll != NULL) \ + (ret) = (pcb)->poll((pcb)->callback_arg,(pcb)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_ERR(errf,arg,err) \ + do { \ + if((errf) != NULL) \ + (errf)((arg),(err)); \ + } while (0) + +#endif /* LWIP_EVENT_API */ + +/** Enabled extra-check for TCP_OVERSIZE if LWIP_DEBUG is enabled */ +#if TCP_OVERSIZE && defined(LWIP_DEBUG) +#define TCP_OVERSIZE_DBGCHECK 1 +#else +#define TCP_OVERSIZE_DBGCHECK 0 +#endif + +/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled */ +#define TCP_CHECKSUM_ON_COPY (LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_TCP) + +/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */ +struct tcp_seg { + struct tcp_seg *next; /* used when putting segements on a queue */ + struct pbuf *p; /* buffer containing data + TCP header */ + void *dataptr; /* pointer to the TCP data in the pbuf */ + u16_t len; /* the TCP length of this segment */ +#if TCP_OVERSIZE_DBGCHECK + u16_t oversize_left; /* Extra bytes available at the end of the last + pbuf in unsent (used for asserting vs. + tcp_pcb.unsent_oversized only) */ +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + u16_t chksum; + u8_t chksum_swapped; +#endif /* TCP_CHECKSUM_ON_COPY */ + u8_t flags; +#define TF_SEG_OPTS_MSS (u8_t)0x01U /* Include MSS option. */ +#define TF_SEG_OPTS_TS (u8_t)0x02U /* Include timestamp option. */ +#define TF_SEG_DATA_CHECKSUMMED (u8_t)0x04U /* ALL data (not the header) is + checksummed into 'chksum' */ + struct tcp_hdr *tcphdr; /* the TCP header */ +}; + +#define LWIP_TCP_OPT_LENGTH(flags) \ + (flags & TF_SEG_OPTS_MSS ? 4 : 0) + \ + (flags & TF_SEG_OPTS_TS ? 12 : 0) + +/** This returns a TCP header option for MSS in an u32_t */ +#define TCP_BUILD_MSS_OPTION(x) (x) = PP_HTONL(((u32_t)2 << 24) | \ + ((u32_t)4 << 16) | \ + (((u32_t)TCP_MSS / 256) << 8) | \ + (TCP_MSS & 255)) + +/* Global variables: */ +extern struct tcp_pcb *tcp_input_pcb; +extern u32_t tcp_ticks; + +/* The TCP PCB lists. */ +union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */ + struct tcp_pcb_listen *listen_pcbs; + struct tcp_pcb *pcbs; +}; +extern struct tcp_pcb *tcp_bound_pcbs; +extern union tcp_listen_pcbs_t tcp_listen_pcbs; +extern struct tcp_pcb *tcp_active_pcbs; /* List of all TCP PCBs that are in a + state in which they accept or send + data. */ +extern struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */ + +extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */ + +/* Axioms about the above lists: + 1) Every TCP PCB that is not CLOSED is in one of the lists. + 2) A PCB is only in one of the lists. + 3) All PCBs in the tcp_listen_pcbs list is in LISTEN state. + 4) All PCBs in the tcp_tw_pcbs list is in TIME-WAIT state. +*/ +/* Define two macros, TCP_REG and TCP_RMV that registers a TCP PCB + with a PCB list or removes a PCB from a list, respectively. */ +#ifndef TCP_DEBUG_PCB_LISTS +#define TCP_DEBUG_PCB_LISTS 0 +#endif +#if TCP_DEBUG_PCB_LISTS +#define TCP_REG(pcbs, npcb) do {\ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_REG %p local port %d\n", (npcb), (npcb)->local_port)); \ + for(tcp_tmp_pcb = *(pcbs); \ + tcp_tmp_pcb != NULL; \ + tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + LWIP_ASSERT("TCP_REG: already registered\n", tcp_tmp_pcb != (npcb)); \ + } \ + LWIP_ASSERT("TCP_REG: pcb->state != CLOSED", ((pcbs) == &tcp_bound_pcbs) || ((npcb)->state != CLOSED)); \ + (npcb)->next = *(pcbs); \ + LWIP_ASSERT("TCP_REG: npcb->next != npcb", (npcb)->next != (npcb)); \ + *(pcbs) = (npcb); \ + LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \ + tcp_timer_needed(); \ + } while(0) +#define TCP_RMV(pcbs, npcb) do { \ + LWIP_ASSERT("TCP_RMV: pcbs != NULL", *(pcbs) != NULL); \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removing %p from %p\n", (npcb), *(pcbs))); \ + if(*(pcbs) == (npcb)) { \ + *(pcbs) = (*pcbs)->next; \ + } else for(tcp_tmp_pcb = *(pcbs); tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next == (npcb)) { \ + tcp_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + (npcb)->next = NULL; \ + LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removed %p from %p\n", (npcb), *(pcbs))); \ + } while(0) + +#else /* LWIP_DEBUG */ + +#define TCP_REG(pcbs, npcb) \ + do { \ + (npcb)->next = *pcbs; \ + *(pcbs) = (npcb); \ + tcp_timer_needed(); \ + } while (0) + +#define TCP_RMV(pcbs, npcb) \ + do { \ + if(*(pcbs) == (npcb)) { \ + (*(pcbs)) = (*pcbs)->next; \ + } \ + else { \ + for(tcp_tmp_pcb = *pcbs; \ + tcp_tmp_pcb != NULL; \ + tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next == (npcb)) { \ + tcp_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + } \ + (npcb)->next = NULL; \ + } while(0) + +#endif /* LWIP_DEBUG */ + + +/* Internal functions: */ +struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +void tcp_pcb_purge(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; + +void tcp_segs_free(struct tcp_seg *seg)ICACHE_FLASH_ATTR; +void tcp_seg_free(struct tcp_seg *seg)ICACHE_FLASH_ATTR; +struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg)ICACHE_FLASH_ATTR; + +#define tcp_ack(pcb) \ + do { \ + if((pcb)->flags & TF_ACK_DELAY) { \ + (pcb)->flags &= ~TF_ACK_DELAY; \ + (pcb)->flags |= TF_ACK_NOW; \ + } \ + else { \ + (pcb)->flags |= TF_ACK_DELAY; \ + } \ + } while (0) + +#define tcp_ack_now(pcb) \ + do { \ + (pcb)->flags |= TF_ACK_NOW; \ + } while (0) + +err_t tcp_send_fin(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +err_t tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)ICACHE_FLASH_ATTR; + +void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg)ICACHE_FLASH_ATTR; + +void tcp_rst(u32_t seqno, u32_t ackno, + ip_addr_t *local_ip, ip_addr_t *remote_ip, + u16_t local_port, u16_t remote_port)ICACHE_FLASH_ATTR; + +u32_t tcp_next_iss(void)ICACHE_FLASH_ATTR; + +void tcp_keepalive(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +void tcp_zero_window_probe(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; + +#if TCP_CALCULATE_EFF_SEND_MSS +u16_t tcp_eff_send_mss(u16_t sendmss, ip_addr_t *addr)ICACHE_FLASH_ATTR; +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +#if LWIP_CALLBACK_API +err_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)ICACHE_FLASH_ATTR; +#endif /* LWIP_CALLBACK_API */ + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +void tcp_debug_print(struct tcp_hdr *tcphdr)ICACHE_FLASH_ATTR; +void tcp_debug_print_flags(u8_t flags)ICACHE_FLASH_ATTR; +void tcp_debug_print_state(enum tcp_state s)ICACHE_FLASH_ATTR; +void tcp_debug_print_pcbs(void)ICACHE_FLASH_ATTR; +s16_t tcp_pcbs_sane(void)ICACHE_FLASH_ATTR; +#else +# define tcp_debug_print(tcphdr) +# define tcp_debug_print_flags(flags) +# define tcp_debug_print_state(s) +# define tcp_debug_print_pcbs() +# define tcp_pcbs_sane() 1 +#endif /* TCP_DEBUG */ + +/** External function (implemented in timers.c), called when TCP detects + * that a timer is needed (i.e. active- or time-wait-pcb found). */ +void tcp_timer_needed(void)ICACHE_FLASH_ATTR; + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* __LWIP_TCP_H__ */ diff --git a/app/include/lwip/tcpip.h b/app/include/lwip/tcpip.h new file mode 100644 index 00000000..995ba8ad --- /dev/null +++ b/app/include/lwip/tcpip.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCPIP_H__ +#define __LWIP_TCPIP_H__ + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api_msg.h" +#include "lwip/netifapi.h" +#include "lwip/pbuf.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Define this to something that triggers a watchdog. This is called from + * tcpip_thread after processing a message. */ +#ifndef LWIP_TCPIP_THREAD_ALIVE +#define LWIP_TCPIP_THREAD_ALIVE() +#endif + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +extern sys_mutex_t lock_tcpip_core; +#define LOCK_TCPIP_CORE() sys_mutex_lock(&lock_tcpip_core) +#define UNLOCK_TCPIP_CORE() sys_mutex_unlock(&lock_tcpip_core) +#define TCPIP_APIMSG(m) tcpip_apimsg_lock(m) +#define TCPIP_APIMSG_ACK(m) +#define TCPIP_NETIFAPI(m) tcpip_netifapi_lock(m) +#define TCPIP_NETIFAPI_ACK(m) +#else /* LWIP_TCPIP_CORE_LOCKING */ +#define LOCK_TCPIP_CORE() +#define UNLOCK_TCPIP_CORE() +#define TCPIP_APIMSG(m) tcpip_apimsg(m) +#define TCPIP_APIMSG_ACK(m) sys_sem_signal(&m->conn->op_completed) +#define TCPIP_NETIFAPI(m) tcpip_netifapi(m) +#define TCPIP_NETIFAPI_ACK(m) sys_sem_signal(&m->sem) +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +/** Function prototype for the init_done function passed to tcpip_init */ +typedef void (*tcpip_init_done_fn)(void *arg); +/** Function prototype for functions passed to tcpip_callback() */ +typedef void (*tcpip_callback_fn)(void *ctx); + +void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg); + +#if LWIP_NETCONN +err_t tcpip_apimsg(struct api_msg *apimsg); +#if LWIP_TCPIP_CORE_LOCKING +err_t tcpip_apimsg_lock(struct api_msg *apimsg); +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETCONN */ + +err_t tcpip_input(struct pbuf *p, struct netif *inp); + +#if LWIP_NETIF_API +err_t tcpip_netifapi(struct netifapi_msg *netifapimsg); +#if LWIP_TCPIP_CORE_LOCKING +err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg); +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETIF_API */ + +err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block); +#define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1) + +/* free pbufs or heap memory from another context without blocking */ +err_t pbuf_free_callback(struct pbuf *p); +err_t mem_free_callback(void *m); + +#if LWIP_TCPIP_TIMEOUT +err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg); +err_t tcpip_untimeout(sys_timeout_handler h, void *arg); +#endif /* LWIP_TCPIP_TIMEOUT */ + +enum tcpip_msg_type { +#if LWIP_NETCONN + TCPIP_MSG_API, +#endif /* LWIP_NETCONN */ + TCPIP_MSG_INPKT, +#if LWIP_NETIF_API + TCPIP_MSG_NETIFAPI, +#endif /* LWIP_NETIF_API */ +#if LWIP_TCPIP_TIMEOUT + TCPIP_MSG_TIMEOUT, + TCPIP_MSG_UNTIMEOUT, +#endif /* LWIP_TCPIP_TIMEOUT */ + TCPIP_MSG_CALLBACK +}; + +struct tcpip_msg { + enum tcpip_msg_type type; + sys_sem_t *sem; + union { +#if LWIP_NETCONN + struct api_msg *apimsg; +#endif /* LWIP_NETCONN */ +#if LWIP_NETIF_API + struct netifapi_msg *netifapimsg; +#endif /* LWIP_NETIF_API */ + struct { + struct pbuf *p; + struct netif *netif; + } inp; + struct { + tcpip_callback_fn function; + void *ctx; + } cb; +#if LWIP_TCPIP_TIMEOUT + struct { + u32_t msecs; + sys_timeout_handler h; + void *arg; + } tmo; +#endif /* LWIP_TCPIP_TIMEOUT */ + } msg; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !NO_SYS */ + +#endif /* __LWIP_TCPIP_H__ */ diff --git a/app/include/lwip/timers.h b/app/include/lwip/timers.h new file mode 100644 index 00000000..e9db02a2 --- /dev/null +++ b/app/include/lwip/timers.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ +#ifndef __LWIP_TIMERS_H__ +#define __LWIP_TIMERS_H__ + +#include "lwip/opt.h" + +/* Timers are not supported when NO_SYS==1 and NO_SYS_NO_TIMERS==1 */ +#define LWIP_TIMERS (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) + +#if LWIP_TIMERS + +#include "lwip/err.h" +#include "lwip/sys.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef LWIP_DEBUG_TIMERNAMES +#ifdef LWIP_DEBUG +#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG +#else /* LWIP_DEBUG */ +#define LWIP_DEBUG_TIMERNAMES 0 +#endif /* LWIP_DEBUG*/ +#endif + +/** Function prototype for a timeout callback function. Register such a function + * using sys_timeout(). + * + * @param arg Additional argument to pass to the function - set up by sys_timeout() + */ +typedef void (* sys_timeout_handler)(void *arg); + +struct sys_timeo { + struct sys_timeo *next; + u32_t time; + sys_timeout_handler h; + void *arg; +#if LWIP_DEBUG_TIMERNAMES + const char* handler_name; +#endif /* LWIP_DEBUG_TIMERNAMES */ +}; + +void sys_timeouts_init(void)ICACHE_FLASH_ATTR; + +#if LWIP_DEBUG_TIMERNAMES +void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name)ICACHE_FLASH_ATTR; +#define sys_timeout(msecs, handler, arg) sys_timeout_debug(msecs, handler, arg, #handler) +#else /* LWIP_DEBUG_TIMERNAMES */ +void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)ICACHE_FLASH_ATTR; +#endif /* LWIP_DEBUG_TIMERNAMES */ + +void sys_untimeout(sys_timeout_handler handler, void *arg)ICACHE_FLASH_ATTR; +#if NO_SYS +void sys_check_timeouts(void)ICACHE_FLASH_ATTR; +void sys_restart_timeouts(void)ICACHE_FLASH_ATTR; +#else /* NO_SYS */ +void sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg); +#endif /* NO_SYS */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TIMERS */ +#endif /* __LWIP_TIMERS_H__ */ diff --git a/app/include/lwip/udp.h b/app/include/lwip/udp.h new file mode 100644 index 00000000..cb53d33e --- /dev/null +++ b/app/include/lwip/udp.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_UDP_H__ +#define __LWIP_UDP_H__ + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define UDP_HLEN 8 + +/* Fields are (of course) in network byte order. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct udp_hdr { + PACK_STRUCT_FIELD(u16_t src); + PACK_STRUCT_FIELD(u16_t dest); /* src/dest UDP ports */ + PACK_STRUCT_FIELD(u16_t len); + PACK_STRUCT_FIELD(u16_t chksum); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define UDP_FLAGS_NOCHKSUM 0x01U +#define UDP_FLAGS_UDPLITE 0x02U +#define UDP_FLAGS_CONNECTED 0x04U +#define UDP_FLAGS_MULTICAST_LOOP 0x08U + +struct udp_pcb; + +/** Function prototype for udp pcb receive callback functions + * addr and port are in same byte order as in the pcb + * The callback is responsible for freeing the pbuf + * if it's not used any more. + * + * ATTENTION: Be aware that 'addr' points into the pbuf 'p' so freeing this pbuf + * makes 'addr' invalid, too. + * + * @param arg user supplied argument (udp_pcb.recv_arg) + * @param pcb the udp_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @param port the remote port from which the packet was received + */ +typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *addr, u16_t port); + + +struct udp_pcb { +/* Common members of all PCB types */ + IP_PCB; + +/* Protocol specific PCB members */ + + struct udp_pcb *next; + + u8_t flags; + /** ports are in host byte order */ + u16_t local_port, remote_port; + +#if LWIP_IGMP + /** outgoing network interface for multicast packets */ + ip_addr_t multicast_ip; +#endif /* LWIP_IGMP */ + +#if LWIP_UDPLITE + /** used for UDP_LITE only */ + u16_t chksum_len_rx, chksum_len_tx; +#endif /* LWIP_UDPLITE */ + + /** receive callback function */ + udp_recv_fn recv; + /** user-supplied argument for the recv callback */ + void *recv_arg; +}; +/* udp_pcbs export for exernal reference (e.g. SNMP agent) */ +extern struct udp_pcb *udp_pcbs; + +/* The following functions is the application layer interface to the + UDP code. */ +struct udp_pcb * udp_new (void)ICACHE_FLASH_ATTR; +void udp_remove (struct udp_pcb *pcb)ICACHE_FLASH_ATTR; +err_t udp_bind (struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port)ICACHE_FLASH_ATTR; +err_t udp_connect (struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port)ICACHE_FLASH_ATTR; +void udp_disconnect (struct udp_pcb *pcb)ICACHE_FLASH_ATTR; +void udp_recv (struct udp_pcb *pcb, udp_recv_fn recv, + void *recv_arg)ICACHE_FLASH_ATTR; +err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif)ICACHE_FLASH_ATTR; +err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port)ICACHE_FLASH_ATTR; +err_t udp_send (struct udp_pcb *pcb, struct pbuf *p)ICACHE_FLASH_ATTR; + +#if LWIP_CHECKSUM_ON_COPY +err_t udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif, u8_t have_chksum, + u16_t chksum)ICACHE_FLASH_ATTR; +err_t udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + u8_t have_chksum, u16_t chksum)ICACHE_FLASH_ATTR; +err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum)ICACHE_FLASH_ATTR; +#endif /* LWIP_CHECKSUM_ON_COPY */ + +#define udp_flags(pcb) ((pcb)->flags) +#define udp_setflags(pcb, f) ((pcb)->flags = (f)) + +/* The following functions are the lower layer interface to UDP. */ +void udp_input (struct pbuf *p, struct netif *inp)ICACHE_FLASH_ATTR; + +#define udp_init() /* Compatibility define, not init needed. */ + +#if UDP_DEBUG +void udp_debug_print(struct udp_hdr *udphdr)ICACHE_FLASH_ATTR; +#else +#define udp_debug_print(udphdr) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_UDP */ + +#endif /* __LWIP_UDP_H__ */ diff --git a/app/include/lwipopts.h b/app/include/lwipopts.h new file mode 100644 index 00000000..90933116 --- /dev/null +++ b/app/include/lwipopts.h @@ -0,0 +1,2052 @@ +/** + * @file + * + * lwIP Options Configuration + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + + +/* + ----------------------------------------------- + ---------- Platform specific locking ---------- + ----------------------------------------------- +*/ + +/** + * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain + * critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#ifndef SYS_LIGHTWEIGHT_PROT +#define SYS_LIGHTWEIGHT_PROT 0 +#endif + +/** + * NO_SYS==1: Provides VERY minimal functionality. Otherwise, + * use lwIP facilities. + */ +#ifndef NO_SYS +#define NO_SYS 1 +#endif + +/** + * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1 + * Mainly for compatibility to old versions. + */ +#ifndef NO_SYS_NO_TIMERS +#define NO_SYS_NO_TIMERS 0 +#endif + +/** + * MEMCPY: override this if you have a faster implementation at hand than the + * one included in your C library + */ +#ifndef MEMCPY +#define MEMCPY(dst,src,len) os_memcpy(dst,src,len) +#endif + +/** + * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a + * call to memcpy() if the length is known at compile time and is small. + */ +#ifndef SMEMCPY +#define SMEMCPY(dst,src,len) os_memcpy(dst,src,len) +#endif + +/* + ------------------------------------ + ---------- Memory options ---------- + ------------------------------------ +*/ +/** + * 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. + */ +#ifndef MEM_LIBC_MALLOC +#define MEM_LIBC_MALLOC 1 +#endif + +/** +* 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! +*/ +#ifndef MEMP_MEM_MALLOC +#define MEMP_MEM_MALLOC 1 +#endif + +/** + * MEM_ALIGNMENT: should be set to the alignment of the CPU + * 4 byte alignment -> #define MEM_ALIGNMENT 4 + * 2 byte alignment -> #define MEM_ALIGNMENT 2 + */ +#ifndef MEM_ALIGNMENT +#define MEM_ALIGNMENT 4 +#endif + +/** + * 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. + */ +#ifndef MEM_SIZE +#define MEM_SIZE 16000 +#endif + +/** + * MEMP_SEPARATE_POOLS: if defined to 1, each pool is placed in its own array. + * This can be used to individually change the location of each pool. + * Default is one big array for all pools + */ +#ifndef MEMP_SEPARATE_POOLS +#define MEMP_SEPARATE_POOLS 1 +#endif + +/** + * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable + * amount of bytes before and after each memp element in every pool and fills + * it with a prominent default value. + * MEMP_OVERFLOW_CHECK == 0 no checking + * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed + * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time + * memp_malloc() or memp_free() is called (useful but slow!) + */ +#ifndef MEMP_OVERFLOW_CHECK +#define MEMP_OVERFLOW_CHECK 0 +#endif + +/** + * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make + * sure that there are no cycles in the linked lists. + */ +#ifndef MEMP_SANITY_CHECK +#define MEMP_SANITY_CHECK 1 +#endif + +/** + * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set + * of memory pools of various sizes. When mem_malloc is called, an element of + * the smallest pool that can provide the length needed is returned. + * To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled. + */ +#ifndef MEM_USE_POOLS +#define MEM_USE_POOLS 0 +#endif + +/** + * MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next + * bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more + * reliable. */ +#ifndef MEM_USE_POOLS_TRY_BIGGER_POOL +#define MEM_USE_POOLS_TRY_BIGGER_POOL 0 +#endif + +/** + * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h + * that defines additional pools beyond the "standard" ones required + * by lwIP. If you set this to 1, you must have lwippools.h in your + * inlude path somewhere. + */ +#ifndef MEMP_USE_CUSTOM_POOLS +#define MEMP_USE_CUSTOM_POOLS 0 +#endif + +/** + * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from + * interrupt context (or another context that doesn't allow waiting for a + * semaphore). + * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT, + * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs + * with each loop so that mem_free can run. + * + * ATTENTION: As you can see from the above description, this leads to dis-/ + * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc + * can need longer. + * + * If you don't want that, at least for NO_SYS=0, you can still use the following + * functions to enqueue a deallocation call which then runs in the tcpip_thread + * context: + * - pbuf_free_callback(p); + * - mem_free_callback(m); + */ +#ifndef LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0 +#endif + +/* + ------------------------------------------------ + ---------- Internal Memory Pool Sizes ---------- + ------------------------------------------------ +*/ +/** + * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF). + * If the application sends a lot of data out of ROM (or other static memory), + * this should be set high. + */ +#ifndef MEMP_NUM_PBUF +#define MEMP_NUM_PBUF 10 +#endif + +/** + * MEMP_NUM_RAW_PCB: Number of raw connection PCBs + * (requires the LWIP_RAW option) + */ +#ifndef MEMP_NUM_RAW_PCB +#define MEMP_NUM_RAW_PCB 4 +#endif + +/** + * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + * per active UDP "connection". + * (requires the LWIP_UDP option) + */ +#ifndef MEMP_NUM_UDP_PCB +#define MEMP_NUM_UDP_PCB 4 +#endif + +/** + * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_PCB +#define MEMP_NUM_TCP_PCB (*(volatile uint32*)0x600011FC) +#endif + +/** + * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_PCB_LISTEN +#define MEMP_NUM_TCP_PCB_LISTEN 2 +#endif + +/** + * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_SEG +#define MEMP_NUM_TCP_SEG 16 +#endif + +/** + * MEMP_NUM_REASSDATA: the number of simultaneously IP packets queued for + * reassembly (whole packets, not fragments!) + */ +#ifndef MEMP_NUM_REASSDATA +#define MEMP_NUM_REASSDATA 0 +#endif + +/** + * MEMP_NUM_FRAG_PBUF: the number of IP fragments simultaneously sent + * (fragments, not whole packets!). + * This is only used with IP_FRAG_USES_STATIC_BUF==0 and + * LWIP_NETIF_TX_SINGLE_PBUF==0 and only has to be > 1 with DMA-enabled MACs + * where the packet is not yet sent when netif->output returns. + */ +#ifndef MEMP_NUM_FRAG_PBUF +#define MEMP_NUM_FRAG_PBUF 0 +#endif + +/** + * MEMP_NUM_ARP_QUEUE: the number of simulateously queued outgoing + * packets (pbufs) that are waiting for an ARP request (to resolve + * their destination address) to finish. + * (requires the ARP_QUEUEING option) + */ +#ifndef MEMP_NUM_ARP_QUEUE +#define MEMP_NUM_ARP_QUEUE 10 +#endif + +/** + * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces + * can be members et the same time (one per netif - allsystems group -, plus one + * per netif membership). + * (requires the LWIP_IGMP option) + */ +#ifndef MEMP_NUM_IGMP_GROUP +#define MEMP_NUM_IGMP_GROUP 8 +#endif + +/** + * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts. + * (requires NO_SYS==0) + */ +#ifndef MEMP_NUM_SYS_TIMEOUT +#define MEMP_NUM_SYS_TIMEOUT 8 +#endif + +/** + * MEMP_NUM_NETBUF: the number of struct netbufs. + * (only needed if you use the sequential API, like api_lib.c) + */ +#ifndef MEMP_NUM_NETBUF +#define MEMP_NUM_NETBUF 0 +#endif + +/** + * MEMP_NUM_NETCONN: the number of struct netconns. + * (only needed if you use the sequential API, like api_lib.c) + */ +#ifndef MEMP_NUM_NETCONN +#define MEMP_NUM_NETCONN 0 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used + * for callback/timeout API communication. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_API +#define MEMP_NUM_TCPIP_MSG_API 4 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used + * for incoming packets. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_INPKT +#define MEMP_NUM_TCPIP_MSG_INPKT 4 +#endif + +/** + * MEMP_NUM_SNMP_NODE: the number of leafs in the SNMP tree. + */ +#ifndef MEMP_NUM_SNMP_NODE +#define MEMP_NUM_SNMP_NODE 0 +#endif + +/** + * MEMP_NUM_SNMP_ROOTNODE: the number of branches in the SNMP tree. + * Every branch has one leaf (MEMP_NUM_SNMP_NODE) at least! + */ +#ifndef MEMP_NUM_SNMP_ROOTNODE +#define MEMP_NUM_SNMP_ROOTNODE 0 +#endif + +/** + * MEMP_NUM_SNMP_VARBIND: the number of concurrent requests (does not have to + * be changed normally) - 2 of these are used per request (1 for input, + * 1 for output) + */ +#ifndef MEMP_NUM_SNMP_VARBIND +#define MEMP_NUM_SNMP_VARBIND 0 +#endif + +/** + * MEMP_NUM_SNMP_VALUE: the number of OID or values concurrently used + * (does not have to be changed normally) - 3 of these are used per request + * (1 for the value read and 2 for OIDs - input and output) + */ +#ifndef MEMP_NUM_SNMP_VALUE +#define MEMP_NUM_SNMP_VALUE 0 +#endif + +/** + * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls + * (before freeing the corresponding memory using lwip_freeaddrinfo()). + */ +#ifndef MEMP_NUM_NETDB +#define MEMP_NUM_NETDB 0 +#endif + +/** + * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list + * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1. + */ +#ifndef MEMP_NUM_LOCALHOSTLIST +#define MEMP_NUM_LOCALHOSTLIST 0 +#endif + +/** + * MEMP_NUM_PPPOE_INTERFACES: the number of concurrently active PPPoE + * interfaces (only used with PPPOE_SUPPORT==1) + */ +#ifndef MEMP_NUM_PPPOE_INTERFACES +#define MEMP_NUM_PPPOE_INTERFACES 0 +#endif + +/** + * PBUF_POOL_SIZE: the number of buffers in the pbuf pool. + */ +#ifndef PBUF_POOL_SIZE +#define PBUF_POOL_SIZE 10 +#endif + +/* + --------------------------------- + ---------- ARP options ---------- + --------------------------------- +*/ +/** + * LWIP_ARP==1: Enable ARP functionality. + */ +#ifndef LWIP_ARP +#define LWIP_ARP 1 +#endif + +/** + * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached. + */ +#ifndef ARP_TABLE_SIZE +#define ARP_TABLE_SIZE 10 +#endif + +/** + * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address + * resolution. By default, only the most recent packet is queued per IP address. + * This is sufficient for most protocols and mainly reduces TCP connection + * startup time. Set this to 1 if you know your application sends more than one + * packet in a row to an IP address that is not in the ARP cache. + */ +#ifndef ARP_QUEUEING +#define ARP_QUEUEING 1 +#endif + +/** + * ETHARP_TRUST_IP_MAC==1: Incoming IP packets cause the ARP table to be + * updated with the source MAC and IP addresses supplied in the packet. + * You may want to disable this if you do not trust LAN peers to have the + * correct addresses, or as a limited approach to attempt to handle + * spoofing. If disabled, lwIP will need to make a new ARP request if + * the peer is not already in the ARP table, adding a little latency. + * The peer *is* in the ARP table if it requested our address before. + * Also notice that this slows down input processing of every IP packet! + */ +#ifndef ETHARP_TRUST_IP_MAC +#define ETHARP_TRUST_IP_MAC 1 +#endif + +/** + * ETHARP_SUPPORT_VLAN==1: support receiving ethernet packets with VLAN header. + * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check. + * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted. + * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted. + */ +#ifndef ETHARP_SUPPORT_VLAN +#define ETHARP_SUPPORT_VLAN 0 +#endif + +/** LWIP_ETHERNET==1: enable ethernet support for PPPoE even though ARP + * might be disabled + */ +#ifndef LWIP_ETHERNET +#define LWIP_ETHERNET (LWIP_ARP || PPPOE_SUPPORT) +#endif + +/** ETH_PAD_SIZE: number of bytes added before the ethernet header to ensure + * alignment of payload after that header. Since the header is 14 bytes long, + * without this padding e.g. addresses in the IP header will not be aligned + * on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms. + */ +#ifndef ETH_PAD_SIZE +#define ETH_PAD_SIZE 0 +#endif + +/** ETHARP_SUPPORT_STATIC_ENTRIES==1: enable code to support static ARP table + * entries (using etharp_add_static_entry/etharp_remove_static_entry). + */ +#ifndef ETHARP_SUPPORT_STATIC_ENTRIES +#define ETHARP_SUPPORT_STATIC_ENTRIES 0 +#endif + + +/* + -------------------------------- + ---------- IP options ---------- + -------------------------------- +*/ +/** + * IP_FORWARD==1: Enables the ability to forward IP packets across network + * interfaces. If you are going to run lwIP on a device with only one network + * interface, define this to 0. + */ +#ifndef IP_FORWARD +#define IP_FORWARD 0 +#endif + +/** + * IP_OPTIONS_ALLOWED: Defines the behavior for IP options. + * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped. + * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed). + */ +#ifndef IP_OPTIONS_ALLOWED +#define IP_OPTIONS_ALLOWED 1 +#endif + +/** + * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that + * this option does not affect outgoing packet sizes, which can be controlled + * via IP_FRAG. + */ +#ifndef IP_REASSEMBLY +#define IP_REASSEMBLY 0 +#endif + +/** + * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note + * that this option does not affect incoming packet sizes, which can be + * controlled via IP_REASSEMBLY. + */ +#ifndef IP_FRAG +#define IP_FRAG 0 +#endif + +/** + * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) + * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived + * in this time, the whole packet is discarded. + */ +#ifndef IP_REASS_MAXAGE +#define IP_REASS_MAXAGE 3 +#endif + +/** + * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled. + * Since the received pbufs are enqueued, be sure to configure + * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive + * packets even if the maximum amount of fragments is enqueued for reassembly! + */ +#ifndef IP_REASS_MAX_PBUFS +#define IP_REASS_MAX_PBUFS 10 +#endif + +/** + * IP_FRAG_USES_STATIC_BUF==1: Use a static MTU-sized buffer for IP + * fragmentation. Otherwise pbufs are allocated and reference the original + * packet data to be fragmented (or with LWIP_NETIF_TX_SINGLE_PBUF==1, + * new PBUF_RAM pbufs are used for fragments). + * ATTENTION: IP_FRAG_USES_STATIC_BUF==1 may not be used for DMA-enabled MACs! + */ +#ifndef IP_FRAG_USES_STATIC_BUF +#define IP_FRAG_USES_STATIC_BUF 1 +#endif + +/** + * IP_FRAG_MAX_MTU: Assumed max MTU on any interface for IP frag buffer + * (requires IP_FRAG_USES_STATIC_BUF==1) + */ +#if IP_FRAG_USES_STATIC_BUF && !defined(IP_FRAG_MAX_MTU) +#define IP_FRAG_MAX_MTU 1500 +#endif + +/** + * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers. + */ +#ifndef IP_DEFAULT_TTL +#define IP_DEFAULT_TTL 255 +#endif + +/** + * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast + * filter per pcb on udp and raw send operations. To enable broadcast filter + * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1. + */ +#ifndef IP_SOF_BROADCAST +#define IP_SOF_BROADCAST 0 +#endif + +/** + * IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast + * filter on recv operations. + */ +#ifndef IP_SOF_BROADCAST_RECV +#define IP_SOF_BROADCAST_RECV 0 +#endif + +/* + ---------------------------------- + ---------- ICMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_ICMP==1: Enable ICMP module inside the IP stack. + * Be careful, disable that make your product non-compliant to RFC1122 + */ +#ifndef LWIP_ICMP +#define LWIP_ICMP 1 +#endif + +/** + * ICMP_TTL: Default value for Time-To-Live used by ICMP packets. + */ +#ifndef ICMP_TTL +#define ICMP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only) + */ +#ifndef LWIP_BROADCAST_PING +#define LWIP_BROADCAST_PING 0 +#endif + +/** + * LWIP_MULTICAST_PING==1: respond to multicast pings (default is unicast only) + */ +#ifndef LWIP_MULTICAST_PING +#define LWIP_MULTICAST_PING 0 +#endif + +/* + --------------------------------- + ---------- RAW options ---------- + --------------------------------- +*/ +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#ifndef LWIP_RAW +#define LWIP_RAW 1 +#endif + +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#ifndef RAW_TTL +#define RAW_TTL (IP_DEFAULT_TTL) +#endif + +/* + ---------------------------------- + ---------- DHCP options ---------- + ---------------------------------- +*/ +/** + * LWIP_DHCP==1: Enable DHCP module. + */ +#ifndef LWIP_DHCP +#define LWIP_DHCP 1 +#endif + +/** + * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address. + */ +#ifndef DHCP_DOES_ARP_CHECK +#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP)) +#endif + +/* + ------------------------------------ + ---------- AUTOIP options ---------- + ------------------------------------ +*/ +/** + * LWIP_AUTOIP==1: Enable AUTOIP module. + */ +#ifndef LWIP_AUTOIP +#define LWIP_AUTOIP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on + * the same interface at the same time. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP +#define LWIP_DHCP_AUTOIP_COOP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes + * that should be sent before falling back on AUTOIP. This can be set + * as low as 1 to get an AutoIP address very quickly, but you should + * be prepared to handle a changing IP address when DHCP overrides + * AutoIP. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP_TRIES +#define LWIP_DHCP_AUTOIP_COOP_TRIES 9 +#endif + +/* + ---------------------------------- + ---------- SNMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_SNMP==1: Turn on SNMP module. UDP must be available for SNMP + * transport. + */ +#ifndef LWIP_SNMP +#define LWIP_SNMP 0 +#endif + +/** + * SNMP_CONCURRENT_REQUESTS: Number of concurrent requests the module will + * allow. At least one request buffer is required. + */ +#ifndef SNMP_CONCURRENT_REQUESTS +#define SNMP_CONCURRENT_REQUESTS 0 +#endif + +/** + * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap + * destination is required + */ +#ifndef SNMP_TRAP_DESTINATIONS +#define SNMP_TRAP_DESTINATIONS 0 +#endif + +/** + * SNMP_PRIVATE_MIB: + */ +#ifndef SNMP_PRIVATE_MIB +#define SNMP_PRIVATE_MIB 0 +#endif + +/** + * Only allow SNMP write actions that are 'safe' (e.g. disabeling netifs is not + * a safe action and disabled when SNMP_SAFE_REQUESTS = 1). + * Unsafe requests are disabled by default! + */ +#ifndef SNMP_SAFE_REQUESTS +#define SNMP_SAFE_REQUESTS 0 +#endif + +/** + * The maximum length of strings used. This affects the size of + * MEMP_SNMP_VALUE elements. + */ +#ifndef SNMP_MAX_OCTET_STRING_LEN +#define SNMP_MAX_OCTET_STRING_LEN 127 +#endif + +/** + * The maximum depth of the SNMP tree. + * With private MIBs enabled, this depends on your MIB! + * This affects the size of MEMP_SNMP_VALUE elements. + */ +#ifndef SNMP_MAX_TREE_DEPTH +#define SNMP_MAX_TREE_DEPTH 15 +#endif + +/** + * The size of the MEMP_SNMP_VALUE elements, normally calculated from + * SNMP_MAX_OCTET_STRING_LEN and SNMP_MAX_TREE_DEPTH. + */ +#ifndef SNMP_MAX_VALUE_SIZE +#define SNMP_MAX_VALUE_SIZE LWIP_MAX((SNMP_MAX_OCTET_STRING_LEN)+1, sizeof(s32_t)*(SNMP_MAX_TREE_DEPTH)) +#endif + +/* + ---------------------------------- + ---------- IGMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_IGMP==1: Turn on IGMP module. + */ +#ifndef LWIP_IGMP +#define LWIP_IGMP 1 +#endif + +/* + ---------------------------------- + ---------- DNS options ----------- + ---------------------------------- +*/ +/** + * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS + * transport. + */ +#ifndef LWIP_DNS +#define LWIP_DNS 1 +#endif + +/** DNS maximum number of entries to maintain locally. */ +#ifndef DNS_TABLE_SIZE +#define DNS_TABLE_SIZE 4 +#endif + +/** DNS maximum host name length supported in the name table. */ +#ifndef DNS_MAX_NAME_LENGTH +#define DNS_MAX_NAME_LENGTH 256 +#endif + +/** The maximum of DNS servers */ +#ifndef DNS_MAX_SERVERS +#define DNS_MAX_SERVERS 2 +#endif + +/** DNS do a name checking between the query and the response. */ +#ifndef DNS_DOES_NAME_CHECK +#define DNS_DOES_NAME_CHECK 1 +#endif + +/** DNS message max. size. Default value is RFC compliant. */ +#ifndef DNS_MSG_SIZE +#define DNS_MSG_SIZE 512 +#endif + +/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled, + * you have to define + * #define DNS_LOCAL_HOSTLIST_INIT {{"host1", 0x123}, {"host2", 0x234}} + * (an array of structs name/address, where address is an u32_t in network + * byte order). + * + * Instead, you can also use an external function: + * #define DNS_LOOKUP_LOCAL_EXTERN(x) extern u32_t my_lookup_function(const char *name) + * that returns the IP address or INADDR_NONE if not found. + */ +#ifndef DNS_LOCAL_HOSTLIST +#define DNS_LOCAL_HOSTLIST 0 +#endif /* DNS_LOCAL_HOSTLIST */ + +/** If this is turned on, the local host-list can be dynamically changed + * at runtime. */ +#ifndef DNS_LOCAL_HOSTLIST_IS_DYNAMIC +#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0 +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/* + --------------------------------- + ---------- UDP options ---------- + --------------------------------- +*/ +/** + * LWIP_UDP==1: Turn on UDP. + */ +#ifndef LWIP_UDP +#define LWIP_UDP 1 +#endif + +/** + * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP) + */ +#ifndef LWIP_UDPLITE +#define LWIP_UDPLITE 0 +#endif + +/** + * UDP_TTL: Default Time-To-Live value. + */ +#ifndef UDP_TTL +#define UDP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_NETBUF_RECVINFO==1: append destination addr and port to every netbuf. + */ +#ifndef LWIP_NETBUF_RECVINFO +#define LWIP_NETBUF_RECVINFO 0 +#endif + +/* + --------------------------------- + ---------- TCP options ---------- + --------------------------------- +*/ +/** + * LWIP_TCP==1: Turn on TCP. + */ +#ifndef LWIP_TCP +#define LWIP_TCP 1 +#endif + +/** + * TCP_TTL: Default Time-To-Live value. + */ +#ifndef TCP_TTL +#define TCP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * TCP_WND: The size of a TCP window. This must be at least + * (2 * TCP_MSS) for things to work well + */ +#ifndef TCP_WND +#define TCP_WND (4 * TCP_MSS) +#endif + +/** + * TCP_MAXRTX: Maximum number of retransmissions of data segments. + */ +#ifndef TCP_MAXRTX +#define TCP_MAXRTX 3 +#endif + +/** + * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments. + */ +#ifndef TCP_SYNMAXRTX +#define TCP_SYNMAXRTX 3 +#endif + +/** + * TCP_MAXRTO: Maximum retransmission timeout of data segments. + */ +#ifndef TCP_MAXRTO +#define TCP_MAXRTO 10 +#endif + +/** + * TCP_MINRTO: Minimum retransmission timeout of data segments. + */ +#ifndef TCP_MINRTO +#define TCP_MINRTO 2 +#endif + +/** + * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order. + * Define to 0 if your device is low on memory. + */ +#ifndef TCP_QUEUE_OOSEQ +#define TCP_QUEUE_OOSEQ 0 +#endif + +/** + * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default, + * you might want to increase this.) + * For the receive side, this MSS is advertised to the remote side + * when opening a connection. For the transmit size, this MSS sets + * an upper limit on the MSS advertised by the remote host. + */ +#ifndef TCP_MSS +#define TCP_MSS 1460 +#endif + +/** + * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really + * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which + * reflects the available reassembly buffer size at the remote host) and the + * largest size permitted by the IP layer" (RFC 1122) + * Setting this to 1 enables code that checks TCP_MSS against the MTU of the + * netif used for a connection and limits the MSS if it would be too big otherwise. + */ +#ifndef TCP_CALCULATE_EFF_SEND_MSS +#define TCP_CALCULATE_EFF_SEND_MSS 1 +#endif + + +/** + * TCP_SND_BUF: TCP sender buffer space (bytes). + */ +#ifndef TCP_SND_BUF +#define TCP_SND_BUF 2 * TCP_MSS +#endif + +/** + * 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. + */ +#ifndef TCP_SND_QUEUELEN +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS)) +#endif + +/** + * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than + * TCP_SND_BUF. It is the amount of space which must be available in the + * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT). + */ +#ifndef TCP_SNDLOWAT +#define TCP_SNDLOWAT ((TCP_SND_BUF)/2) +#endif + +/** + * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be grater + * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below + * this number, select returns writable (combined with TCP_SNDLOWAT). + */ +#ifndef TCP_SNDQUEUELOWAT +#define TCP_SNDQUEUELOWAT LWIP_MAX(((TCP_SND_QUEUELEN)/2), 5) +#endif + +/** + * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb. + */ +#ifndef TCP_LISTEN_BACKLOG +#define TCP_LISTEN_BACKLOG 0 +#endif + +/** + * The maximum allowed backlog for TCP listen netconns. + * This backlog is used unless another is explicitly specified. + * 0xff is the maximum (u8_t). + */ +#ifndef TCP_DEFAULT_LISTEN_BACKLOG +#define TCP_DEFAULT_LISTEN_BACKLOG 0xff +#endif + +/** + * TCP_OVERSIZE: The maximum number of bytes that tcp_write may + * allocate ahead of time in an attempt to create shorter pbuf chains + * for transmission. The meaningful range is 0 to TCP_MSS. Some + * suggested values are: + * + * 0: Disable oversized allocation. Each tcp_write() allocates a new + pbuf (old behaviour). + * 1: Allocate size-aligned pbufs with minimal excess. Use this if your + * scatter-gather DMA requires aligned fragments. + * 128: Limit the pbuf/memory overhead to 20%. + * TCP_MSS: Try to create unfragmented TCP packets. + * TCP_MSS/4: Try to create 4 fragments or less per TCP packet. + */ +#ifndef TCP_OVERSIZE +#define TCP_OVERSIZE TCP_MSS +#endif + +/** + * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option. + */ +#ifndef LWIP_TCP_TIMESTAMPS +#define LWIP_TCP_TIMESTAMPS 0 +#endif + +/** + * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an + * explicit window update + */ +#ifndef TCP_WND_UPDATE_THRESHOLD +#define TCP_WND_UPDATE_THRESHOLD (TCP_WND / 4) +#endif + +/** + * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1. + * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all + * events (accept, sent, etc) that happen in the system. + * LWIP_CALLBACK_API==1: The PCB callback function is called directly + * for the event. + */ +#ifndef LWIP_EVENT_API +#define LWIP_EVENT_API 0 +#define LWIP_CALLBACK_API 1 +#else +#define LWIP_EVENT_API 1 +#define LWIP_CALLBACK_API 0 +#endif + + +/* + ---------------------------------- + ---------- Pbuf options ---------- + ---------------------------------- +*/ +/** + * PBUF_LINK_HLEN: the number of bytes that should be allocated for a + * link level header. The default is 14, the standard value for + * Ethernet. + */ +#ifndef PBUF_LINK_HLEN +#define PBUF_LINK_HLEN (14 + ETH_PAD_SIZE) +#endif + +/** + * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is + * designed to accomodate single full size TCP frame in one pbuf, including + * TCP_MSS, IP header, and link header. + */ +#ifndef PBUF_POOL_BUFSIZE +#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN) +#endif + +/* + ------------------------------------------------ + ---------- Network Interfaces options ---------- + ------------------------------------------------ +*/ +/** + * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname + * field. + */ +#ifndef LWIP_NETIF_HOSTNAME +#define LWIP_NETIF_HOSTNAME 0 +#endif + +/** + * LWIP_NETIF_API==1: Support netif api (in netifapi.c) + */ +#ifndef LWIP_NETIF_API +#define LWIP_NETIF_API 0 +#endif + +/** + * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface + * changes its up/down status (i.e., due to DHCP IP acquistion) + */ +#ifndef LWIP_NETIF_STATUS_CALLBACK +#define LWIP_NETIF_STATUS_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface + * whenever the link changes (i.e., link down) + */ +#ifndef LWIP_NETIF_LINK_CALLBACK +#define LWIP_NETIF_LINK_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table + * indices) in struct netif. TCP and UDP can make use of this to prevent + * scanning the ARP table for every sent packet. While this is faster for big + * ARP tables or many concurrent connections, it might be counterproductive + * if you have a tiny ARP table or if there never are concurrent connections. + */ +#ifndef LWIP_NETIF_HWADDRHINT +#define LWIP_NETIF_HWADDRHINT 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP + * address equal to the netif IP address, looping them back up the stack. + */ +#ifndef LWIP_NETIF_LOOPBACK +#define LWIP_NETIF_LOOPBACK 0 +#endif + +/** + * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback + * sending for each netif (0 = disabled) + */ +#ifndef LWIP_LOOPBACK_MAX_PBUFS +#define LWIP_LOOPBACK_MAX_PBUFS 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in + * the system, as netifs must change how they behave depending on this setting + * for the LWIP_NETIF_LOOPBACK option to work. + * Setting this is needed to avoid reentering non-reentrant functions like + * tcp_input(). + * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a + * multithreaded environment like tcpip.c. In this case, netif->input() + * is called directly. + * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup. + * The packets are put on a list and netif_poll() must be called in + * the main application loop. + */ +#ifndef LWIP_NETIF_LOOPBACK_MULTITHREADING +#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS) +#endif + +/** + * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data + * to be sent into one single pbuf. This is for compatibility with DMA-enabled + * MACs that do not support scatter-gather. + * Beware that this might involve CPU-memcpy before transmitting that would not + * be needed without this flag! Use this only if you need to! + * + * @todo: TCP and IP-frag do not work with this, yet: + */ +#ifndef LWIP_NETIF_TX_SINGLE_PBUF +#define LWIP_NETIF_TX_SINGLE_PBUF 1 +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + +/* + ------------------------------------ + ---------- LOOPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1) and loopif.c + */ +#ifndef LWIP_HAVE_LOOPIF +#define LWIP_HAVE_LOOPIF 0 +#endif + +/* + ------------------------------------ + ---------- SLIPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_SLIPIF==1: Support slip interface and slipif.c + */ +#ifndef LWIP_HAVE_SLIPIF +#define LWIP_HAVE_SLIPIF 0 +#endif + +/* + ------------------------------------ + ---------- Thread options ---------- + ------------------------------------ +*/ +/** + * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread. + */ +#ifndef TCPIP_THREAD_NAME +#define TCPIP_THREAD_NAME "tcpip_thread" +#endif + +/** + * 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. + */ +#ifndef TCPIP_THREAD_STACKSIZE +#define TCPIP_THREAD_STACKSIZE 0 +#endif + +/** + * 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. + */ +#ifndef TCPIP_THREAD_PRIO +#define TCPIP_THREAD_PRIO 1 +#endif + +/** + * 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. + */ +#ifndef TCPIP_MBOX_SIZE +#define TCPIP_MBOX_SIZE 0 +#endif + +/** + * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread. + */ +#ifndef SLIPIF_THREAD_NAME +#define SLIPIF_THREAD_NAME "slipif_loop" +#endif + +/** + * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_STACKSIZE +#define SLIPIF_THREAD_STACKSIZE 0 +#endif + +/** + * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_PRIO +#define SLIPIF_THREAD_PRIO 1 +#endif + +/** + * PPP_THREAD_NAME: The name assigned to the pppInputThread. + */ +#ifndef PPP_THREAD_NAME +#define PPP_THREAD_NAME "pppInputThread" +#endif + +/** + * PPP_THREAD_STACKSIZE: The stack size used by the pppInputThread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_STACKSIZE +#define PPP_THREAD_STACKSIZE 0 +#endif + +/** + * PPP_THREAD_PRIO: The priority assigned to the pppInputThread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_PRIO +#define PPP_THREAD_PRIO 1 +#endif + +/** + * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread. + */ +#ifndef DEFAULT_THREAD_NAME +#define DEFAULT_THREAD_NAME "lwIP" +#endif + +/** + * 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. + */ +#ifndef DEFAULT_THREAD_STACKSIZE +#define DEFAULT_THREAD_STACKSIZE 0 +#endif + +/** + * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef DEFAULT_THREAD_PRIO +#define DEFAULT_THREAD_PRIO 1 +#endif + +/** + * 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. + */ +#ifndef DEFAULT_RAW_RECVMBOX_SIZE +#define DEFAULT_RAW_RECVMBOX_SIZE 0 +#endif + +/** + * 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. + */ +#ifndef DEFAULT_UDP_RECVMBOX_SIZE +#define DEFAULT_UDP_RECVMBOX_SIZE 0 +#endif + +/** + * 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. + */ +#ifndef DEFAULT_TCP_RECVMBOX_SIZE +#define DEFAULT_TCP_RECVMBOX_SIZE 0 +#endif + +/** + * 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. + */ +#ifndef DEFAULT_ACCEPTMBOX_SIZE +#define DEFAULT_ACCEPTMBOX_SIZE 0 +#endif + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * LWIP_TCPIP_CORE_LOCKING: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#ifndef LWIP_TCPIP_CORE_LOCKING +#define LWIP_TCPIP_CORE_LOCKING 0 +#endif + +/** + * LWIP_TCPIP_CORE_LOCKING_INPUT: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#ifndef LWIP_TCPIP_CORE_LOCKING_INPUT +#define LWIP_TCPIP_CORE_LOCKING_INPUT 0 +#endif + +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#ifndef LWIP_NETCONN +#define LWIP_NETCONN 0 +#endif + +/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout tod create + * timers running in tcpip_thread from another thread. + */ +#ifndef LWIP_TCPIP_TIMEOUT +#define LWIP_TCPIP_TIMEOUT 1 +#endif + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 0 +#endif + +/** + * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names. + * (only used if you use sockets.c) + */ +#ifndef LWIP_COMPAT_SOCKETS +#define LWIP_COMPAT_SOCKETS 0 +#endif + +/** + * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names. + * Disable this option if you use a POSIX operating system that uses the same + * names (read, write & close). (only used if you use sockets.c) + */ +#ifndef LWIP_POSIX_SOCKETS_IO_NAMES +#define LWIP_POSIX_SOCKETS_IO_NAMES 0 +#endif + +/** + * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT + * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set + * in seconds. (does not require sockets.c, and will affect tcp.c) + */ +#ifndef LWIP_TCP_KEEPALIVE +#define LWIP_TCP_KEEPALIVE 1 +#endif + +/** + * LWIP_SO_RCVTIMEO==1: Enable SO_RCVTIMEO processing. + */ +#ifndef LWIP_SO_RCVTIMEO +#define LWIP_SO_RCVTIMEO 0 +#endif + +/** + * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. + */ +#ifndef LWIP_SO_RCVBUF +#define LWIP_SO_RCVBUF 0 +#endif + +/** + * If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize. + */ +#ifndef RECV_BUFSIZE_DEFAULT +#define RECV_BUFSIZE_DEFAULT INT_MAX +#endif + +/** + * SO_REUSE==1: Enable SO_REUSEADDR option. + */ +#ifndef SO_REUSE +#define SO_REUSE 0 +#endif + +/** + * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets + * to all local matches if SO_REUSEADDR is turned on. + * WARNING: Adds a memcpy for every packet if passing to more than one pcb! + */ +#ifndef SO_REUSE_RXTOALL +#define SO_REUSE_RXTOALL 0 +#endif + +/* + ---------------------------------------- + ---------- Statistics options ---------- + ---------------------------------------- +*/ +/** + * LWIP_STATS==1: Enable statistics collection in lwip_stats. + */ +#ifndef LWIP_STATS +#define LWIP_STATS 0 +#endif + +#if LWIP_STATS + +/** + * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions. + */ +#ifndef LWIP_STATS_DISPLAY +#define LWIP_STATS_DISPLAY 0 +#endif + +/** + * LINK_STATS==1: Enable link stats. + */ +#ifndef LINK_STATS +#define LINK_STATS 1 +#endif + +/** + * ETHARP_STATS==1: Enable etharp stats. + */ +#ifndef ETHARP_STATS +#define ETHARP_STATS (LWIP_ARP) +#endif + +/** + * IP_STATS==1: Enable IP stats. + */ +#ifndef IP_STATS +#define IP_STATS 1 +#endif + +/** + * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is + * on if using either frag or reass. + */ +#ifndef IPFRAG_STATS +#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG) +#endif + +/** + * ICMP_STATS==1: Enable ICMP stats. + */ +#ifndef ICMP_STATS +#define ICMP_STATS 1 +#endif + +/** + * IGMP_STATS==1: Enable IGMP stats. + */ +#ifndef IGMP_STATS +#define IGMP_STATS (LWIP_IGMP) +#endif + +/** + * UDP_STATS==1: Enable UDP stats. Default is on if + * UDP enabled, otherwise off. + */ +#ifndef UDP_STATS +#define UDP_STATS (LWIP_UDP) +#endif + +/** + * TCP_STATS==1: Enable TCP stats. Default is on if TCP + * enabled, otherwise off. + */ +#ifndef TCP_STATS +#define TCP_STATS (LWIP_TCP) +#endif + +/** + * MEM_STATS==1: Enable mem.c stats. + */ +#ifndef MEM_STATS +#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0)) +#endif + +/** + * MEMP_STATS==1: Enable memp.c pool stats. + */ +#ifndef MEMP_STATS +#define MEMP_STATS (MEMP_MEM_MALLOC == 0) +#endif + +/** + * SYS_STATS==1: Enable system stats (sem and mbox counts, etc). + */ +#ifndef SYS_STATS +#define SYS_STATS (NO_SYS == 0) +#endif + +#else +#define ETHARP_STATS 0 +#define LINK_STATS 0 +#define IP_STATS 0 +#define IPFRAG_STATS 0 +#define ICMP_STATS 0 +#define IGMP_STATS 0 +#define UDP_STATS 0 +#define TCP_STATS 0 +#define MEM_STATS 0 +#define MEMP_STATS 0 +#define SYS_STATS 0 +#define LWIP_STATS_DISPLAY 0 + +#endif /* LWIP_STATS */ + +/* + --------------------------------- + ---------- PPP options ---------- + --------------------------------- +*/ +/** + * PPP_SUPPORT==1: Enable PPP. + */ +#ifndef PPP_SUPPORT +#define PPP_SUPPORT 0 +#endif + +/** + * PPPOE_SUPPORT==1: Enable PPP Over Ethernet + */ +#ifndef PPPOE_SUPPORT +#define PPPOE_SUPPORT 0 +#endif + +/** + * PPPOS_SUPPORT==1: Enable PPP Over Serial + */ +#ifndef PPPOS_SUPPORT +#define PPPOS_SUPPORT PPP_SUPPORT +#endif + +#if PPP_SUPPORT + +/** + * NUM_PPP: Max PPP sessions. + */ +#ifndef NUM_PPP +#define NUM_PPP 1 +#endif + +/** + * PAP_SUPPORT==1: Support PAP. + */ +#ifndef PAP_SUPPORT +#define PAP_SUPPORT 0 +#endif + +/** + * CHAP_SUPPORT==1: Support CHAP. + */ +#ifndef CHAP_SUPPORT +#define CHAP_SUPPORT 0 +#endif + +/** + * MSCHAP_SUPPORT==1: Support MSCHAP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef MSCHAP_SUPPORT +#define MSCHAP_SUPPORT 0 +#endif + +/** + * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CBCP_SUPPORT +#define CBCP_SUPPORT 0 +#endif + +/** + * CCP_SUPPORT==1: Support CCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CCP_SUPPORT +#define CCP_SUPPORT 0 +#endif + +/** + * VJ_SUPPORT==1: Support VJ header compression. + */ +#ifndef VJ_SUPPORT +#define VJ_SUPPORT 0 +#endif + +/** + * MD5_SUPPORT==1: Support MD5 (see also CHAP). + */ +#ifndef MD5_SUPPORT +#define MD5_SUPPORT 0 +#endif + +/* + * Timeouts + */ +#ifndef FSM_DEFTIMEOUT +#define FSM_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef FSM_DEFMAXTERMREQS +#define FSM_DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXCONFREQS +#define FSM_DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXNAKLOOPS +#define FSM_DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ +#endif + +#ifndef UPAP_DEFTIMEOUT +#define UPAP_DEFTIMEOUT 6 /* Timeout (seconds) for retransmitting req */ +#endif + +#ifndef UPAP_DEFREQTIME +#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif + +#ifndef CHAP_DEFTIMEOUT +#define CHAP_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef CHAP_DEFTRANSMITS +#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ +#endif + +/* Interval in seconds between keepalive echo requests, 0 to disable. */ +#ifndef LCP_ECHOINTERVAL +#define LCP_ECHOINTERVAL 0 +#endif + +/* Number of unanswered echo requests before failure. */ +#ifndef LCP_MAXECHOFAILS +#define LCP_MAXECHOFAILS 3 +#endif + +/* Max Xmit idle time (in jiffies) before resend flag char. */ +#ifndef PPP_MAXIDLEFLAG +#define PPP_MAXIDLEFLAG 100 +#endif + +/* + * Packet sizes + * + * Note - lcp shouldn't be allowed to negotiate stuff outside these + * limits. See lcp.h in the pppd directory. + * (XXX - these constants should simply be shared by lcp.c instead + * of living in lcp.h) + */ +#define PPP_MTU 1500 /* Default MTU (size of Info field) */ +#ifndef PPP_MAXMTU +/* #define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) */ +#define PPP_MAXMTU 1500 /* Largest MTU we allow */ +#endif +#define PPP_MINMTU 64 +#define PPP_MRU 1500 /* default MRU = max length of info field */ +#define PPP_MAXMRU 1500 /* Largest MRU we allow */ +#ifndef PPP_DEFMRU +#define PPP_DEFMRU 296 /* Try for this */ +#endif +#define PPP_MINMRU 128 /* No MRUs below this */ + +#ifndef MAXNAMELEN +#define MAXNAMELEN 256 /* max length of hostname or name for auth */ +#endif +#ifndef MAXSECRETLEN +#define MAXSECRETLEN 256 /* max length of password or secret */ +#endif + +#endif /* PPP_SUPPORT */ + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ +/** + * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets. + */ +#ifndef CHECKSUM_GEN_IP +#define CHECKSUM_GEN_IP 1 +#endif + +/** + * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets. + */ +#ifndef CHECKSUM_GEN_UDP +#define CHECKSUM_GEN_UDP 1 +#endif + +/** + * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets. + */ +#ifndef CHECKSUM_GEN_TCP +#define CHECKSUM_GEN_TCP 1 +#endif + +/** + * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets. + */ +#ifndef CHECKSUM_CHECK_IP +#define CHECKSUM_CHECK_IP 1 +#endif + +/** + * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets. + */ +#ifndef CHECKSUM_CHECK_UDP +#define CHECKSUM_CHECK_UDP 1 +#endif + +/** + * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets. + */ +#ifndef CHECKSUM_CHECK_TCP +#define CHECKSUM_CHECK_TCP 1 +#endif + +/** + * LWIP_CHECKSUM_ON_COPY==1: Calculate checksum when copying data from + * application buffers to pbufs. + */ +#ifndef LWIP_CHECKSUM_ON_COPY +#define LWIP_CHECKSUM_ON_COPY 0 +#endif + +/* + --------------------------------------- + ---------- Debugging options ---------- + --------------------------------------- +*/ +/** + * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is + * compared against this value. If it is smaller, then debugging + * messages are written. + */ +#ifndef LWIP_DBG_MIN_LEVEL +#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL +#endif + +/** + * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable + * debug messages of certain types. + */ +#ifndef LWIP_DBG_TYPES_ON +#define LWIP_DBG_TYPES_ON LWIP_DBG_OFF +#endif + +/** + * ETHARP_DEBUG: Enable debugging in etharp.c. + */ +#ifndef ETHARP_DEBUG +#define ETHARP_DEBUG LWIP_DBG_OFF +#endif + +/** + * NETIF_DEBUG: Enable debugging in netif.c. + */ +#ifndef NETIF_DEBUG +#define NETIF_DEBUG LWIP_DBG_OFF +#endif + +/** + * PBUF_DEBUG: Enable debugging in pbuf.c. + */ +#ifndef PBUF_DEBUG +#define PBUF_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_LIB_DEBUG: Enable debugging in api_lib.c. + */ +#ifndef API_LIB_DEBUG +#define API_LIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_MSG_DEBUG: Enable debugging in api_msg.c. + */ +#ifndef API_MSG_DEBUG +#define API_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SOCKETS_DEBUG: Enable debugging in sockets.c. + */ +#ifndef SOCKETS_DEBUG +#define SOCKETS_DEBUG LWIP_DBG_OFF +#endif + +/** + * ICMP_DEBUG: Enable debugging in icmp.c. + */ +#ifndef ICMP_DEBUG +#define ICMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IGMP_DEBUG: Enable debugging in igmp.c. + */ +#ifndef IGMP_DEBUG +#define IGMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * INET_DEBUG: Enable debugging in inet.c. + */ +#ifndef INET_DEBUG +#define INET_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_DEBUG: Enable debugging for IP. + */ +#ifndef IP_DEBUG +#define IP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass. + */ +#ifndef IP_REASS_DEBUG +#define IP_REASS_DEBUG LWIP_DBG_OFF +#endif + +/** + * RAW_DEBUG: Enable debugging in raw.c. + */ +#ifndef RAW_DEBUG +#define RAW_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEM_DEBUG: Enable debugging in mem.c. + */ +#ifndef MEM_DEBUG +#define MEM_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEMP_DEBUG: Enable debugging in memp.c. + */ +#ifndef MEMP_DEBUG +#define MEMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SYS_DEBUG: Enable debugging in sys.c. + */ +#ifndef SYS_DEBUG +#define SYS_DEBUG LWIP_DBG_OFF +#endif + +/** + * TIMERS_DEBUG: Enable debugging in timers.c. + */ +#ifndef TIMERS_DEBUG +#define TIMERS_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_DEBUG: Enable debugging for TCP. + */ +#ifndef TCP_DEBUG +#define TCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug. + */ +#ifndef TCP_INPUT_DEBUG +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit. + */ +#ifndef TCP_FR_DEBUG +#define TCP_FR_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit + * timeout. + */ +#ifndef TCP_RTO_DEBUG +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_CWND_DEBUG: Enable debugging for TCP congestion window. + */ +#ifndef TCP_CWND_DEBUG +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating. + */ +#ifndef TCP_WND_DEBUG +#define TCP_WND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions. + */ +#ifndef TCP_OUTPUT_DEBUG +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RST_DEBUG: Enable debugging for TCP with the RST message. + */ +#ifndef TCP_RST_DEBUG +#define TCP_RST_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths. + */ +#ifndef TCP_QLEN_DEBUG +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#endif + +/** + * UDP_DEBUG: Enable debugging in UDP. + */ +#ifndef UDP_DEBUG +#define UDP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCPIP_DEBUG: Enable debugging in tcpip.c. + */ +#ifndef TCPIP_DEBUG +#define TCPIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * PPP_DEBUG: Enable debugging for PPP. + */ +#ifndef PPP_DEBUG +#define PPP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SLIP_DEBUG: Enable debugging in slipif.c. + */ +#ifndef SLIP_DEBUG +#define SLIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * DHCP_DEBUG: Enable debugging in dhcp.c. + */ +#ifndef DHCP_DEBUG +#define DHCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * AUTOIP_DEBUG: Enable debugging in autoip.c. + */ +#ifndef AUTOIP_DEBUG +#define AUTOIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MSG_DEBUG: Enable debugging for SNMP messages. + */ +#ifndef SNMP_MSG_DEBUG +#define SNMP_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs. + */ +#ifndef SNMP_MIB_DEBUG +#define SNMP_MIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * DNS_DEBUG: Enable debugging for DNS. + */ +#ifndef DNS_DEBUG +#define DNS_DEBUG LWIP_DBG_OFF +#endif + +#ifndef TCP_MSL +#define TCP_MSL 2000UL /* The maximum segment lifetime in milliseconds */ +#endif + +#endif /* __LWIP_OPT_H__ */ diff --git a/app/include/mem_manager.h b/app/include/mem_manager.h new file mode 100644 index 00000000..185cc4a7 --- /dev/null +++ b/app/include/mem_manager.h @@ -0,0 +1,81 @@ +#ifndef __MEM_MANAGER_H__ +#define __MEM_MANAGER_H__ + +#include "c_types.h" + +/*------------------------------------------------*/ + +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE +#ifndef IOT_SIP_MODE +//#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 0x3fffc000 - (uint32)&_heap_start ) )//fix 16000 to 24000 on 14.2.26 +#else +#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 8000 ) ) +#endif +#define portBYTE_ALIGNMENT 8 +#define pdFALSE 0 +#define pdTRUE 1 + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE +#if portBYTE_ALIGNMENT == 8 + #define portBYTE_ALIGNMENT_MASK ( 0x0007 ) +#endif + +#if portBYTE_ALIGNMENT == 4 + #define portBYTE_ALIGNMENT_MASK ( 0x0003 ) +#endif + +#if portBYTE_ALIGNMENT == 2 + #define portBYTE_ALIGNMENT_MASK ( 0x0001 ) +#endif + +#if portBYTE_ALIGNMENT == 1 + #define portBYTE_ALIGNMENT_MASK ( 0x0000 ) +#endif + +#ifndef portBYTE_ALIGNMENT_MASK + #error "Invalid portBYTE_ALIGNMENT definition" +#endif + +#define configUSE_MALLOC_FAILED_HOOK 1 +#define portPOINTER_SIZE_TYPE unsigned int + +#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( heapSTRUCT_SIZE * 2 ) ) + +//#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) + +//static unsigned char ucHeap[ configTOTAL_HEAP_SIZE ]; +static unsigned char *ucHeap; + +typedef struct A_BLOCK_LINK +{ + struct A_BLOCK_LINK *pxNextFreeBlock; //The next free block in the list. + size_t xBlockSize; //The size of the free block. +} xBlockLink; + +static const unsigned short heapSTRUCT_SIZE = ( sizeof( xBlockLink ) + portBYTE_ALIGNMENT - ( sizeof( xBlockLink ) % portBYTE_ALIGNMENT ) ); + +//static const size_t xTotalHeapSize = ( ( size_t ) configADJUSTED_HEAP_SIZE ) & ( ( size_t ) ~portBYTE_ALIGNMENT_MASK ); + +static xBlockLink xStart, *pxEnd = NULL; + +//static size_t xFreeBytesRemaining = ( ( size_t ) configADJUSTED_HEAP_SIZE ) & ( ( size_t ) ~portBYTE_ALIGNMENT_MASK ); + + +/*-----------------------------------------------------------*/ + +static void prvInsertBlockIntoFreeList( xBlockLink *pxBlockToInsert ) ;//ICACHE_FLASH_ATTR; + +static void prvHeapInit( void ) ;//ICACHE_FLASH_ATTR; + +void vApplicationMallocFailedHook( void ) ;//ICACHE_FLASH_ATTR; + +void *pvPortMalloc( size_t xWantedSize ) ;//ICACHE_FLASH_ATTR; + +void vPortFree( void *pv ) ;//ICACHE_FLASH_ATTR; + +size_t xPortGetFreeHeapSize( void ) ;//ICACHE_FLASH_ATTR; + +void vPortInitialiseBlocks( void ) ;//ICACHE_FLASH_ATTR; +/*-----------------------------------------------------------*/ + +#endif diff --git a/app/include/netif/etharp.h b/app/include/netif/etharp.h new file mode 100644 index 00000000..2092ab7a --- /dev/null +++ b/app/include/netif/etharp.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __NETIF_ETHARP_H__ +#define __NETIF_ETHARP_H__ + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ETHARP_HWADDR_LEN +#define ETHARP_HWADDR_LEN 6 +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_addr { + PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** Ethernet header */ +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FIELD(struct eth_addr dest); + PACK_STRUCT_FIELD(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +#if ETHARP_SUPPORT_VLAN + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** VLAN header inserted between ethernet header and payload + * if 'type' in ethernet header is ETHTYPE_VLAN. + * See IEEE802.Q */ +struct eth_vlan_hdr { + PACK_STRUCT_FIELD(u16_t tpid); + PACK_STRUCT_FIELD(u16_t prio_vid); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_VLAN_HDR 4 +#define VLAN_ID(vlan_hdr) (htons((vlan_hdr)->prio_vid) & 0xFFF) + +#endif /* ETHARP_SUPPORT_VLAN */ + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** the ARP message, see RFC 826 ("Packet format") */ +struct etharp_hdr { + PACK_STRUCT_FIELD(u16_t hwtype); + PACK_STRUCT_FIELD(u16_t proto); + PACK_STRUCT_FIELD(u8_t hwlen); + PACK_STRUCT_FIELD(u8_t protolen); + PACK_STRUCT_FIELD(u16_t opcode); + PACK_STRUCT_FIELD(struct eth_addr shwaddr); + PACK_STRUCT_FIELD(struct ip_addr2 sipaddr); + PACK_STRUCT_FIELD(struct eth_addr dhwaddr); + PACK_STRUCT_FIELD(struct ip_addr2 dipaddr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETHARP_HDR 28 +#define SIZEOF_ETHARP_MINSIZE 46 +#define SIZEOF_ETHARP_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR) +#define SIZEOF_ETHARP_WITHPAD (SIZEOF_ETH_HDR + SIZEOF_ETHARP_MINSIZE) + +/** 5 seconds period */ +#define ARP_TMR_INTERVAL 5000 + +#define ETHTYPE_ARP 0x0806 +#define ETHTYPE_IP 0x0800 +#define ETHTYPE_VLAN 0x8100 +#define ETHTYPE_PPPOEDISC 0x8863 /* PPP Over Ethernet Discovery Stage */ +#define ETHTYPE_PPPOE 0x8864 /* PPP Over Ethernet Session Stage */ +#define ETHTYPE_PAE 0x888e + +/** MEMCPY-like macro to copy to/from struct eth_addr's that are local variables + * or known to be 32-bit aligned within the protocol header. */ +#ifndef ETHADDR32_COPY +#define ETHADDR32_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN) +#endif + +/** MEMCPY-like macro to copy to/from struct eth_addr's that are no local + * variables and known to be 16-bit aligned within the protocol header. */ +#ifndef ETHADDR16_COPY +#define ETHADDR16_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN) +#endif + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +/** ARP message types (opcodes) */ +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +/** Define this to 1 and define LWIP_ARP_FILTER_NETIF_FN(pbuf, netif, type) + * to a filter function that returns the correct netif when using multiple + * netifs on one hardware interface where the netif's low-level receive + * routine cannot decide for the correct netif (e.g. when mapping multiple + * IP addresses to one hardware interface). + */ +#ifndef LWIP_ARP_FILTER_NETIF +#define LWIP_ARP_FILTER_NETIF 0 +#endif + +#if ARP_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct etharp_q_entry { + struct etharp_q_entry *next; + struct pbuf *p; +}; +#endif /* ARP_QUEUEING */ + +#define etharp_init() /* Compatibility define, not init needed. */ +void etharp_tmr(void)ICACHE_FLASH_ATTR; +s8_t etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, + struct eth_addr **eth_ret, ip_addr_t **ip_ret)ICACHE_FLASH_ATTR; +err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)ICACHE_FLASH_ATTR; +err_t etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)ICACHE_FLASH_ATTR; +err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr)ICACHE_FLASH_ATTR; +/** For Ethernet network interfaces, we might want to send "gratuitous ARP"; + * this is an ARP packet sent by a node in order to spontaneously cause other + * nodes to update an entry in their ARP cache. + * From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */ +#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr) +void etharp_cleanup_netif(struct netif *netif); + +#if ETHARP_SUPPORT_STATIC_ENTRIES +err_t etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr)ICACHE_FLASH_ATTR; +err_t etharp_remove_static_entry(ip_addr_t *ipaddr)ICACHE_FLASH_ATTR; +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +#if LWIP_AUTOIP +err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr, + const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr, + const u16_t opcode)ICACHE_FLASH_ATTR; +#endif /* LWIP_AUTOIP */ + +#endif /* LWIP_ARP */ + +err_t ethernet_input(struct pbuf *p, struct netif *netif)ICACHE_FLASH_ATTR; + +#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETHARP_HWADDR_LEN) == 0) + +extern const struct eth_addr ethbroadcast, ethzero; + +#endif /* LWIP_ARP || LWIP_ETHERNET */ + +#if 0 +/** Ethernet header */ +#ifndef ETHARP_HWADDR_LEN +#define ETHARP_HWADDR_LEN 6 +#endif + + +struct eth_addr { + PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; + + +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FIELD(struct eth_addr dest); + PACK_STRUCT_FIELD(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __NETIF_ARP_H__ */ diff --git a/app/include/netif/if_llc.h b/app/include/netif/if_llc.h new file mode 100644 index 00000000..ca09b386 --- /dev/null +++ b/app/include/netif/if_llc.h @@ -0,0 +1,173 @@ +/* $NetBSD: if_llc.h,v 1.12 1999/11/19 20:41:19 thorpej Exp $ */ + +/*- + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_llc.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD$ + */ + +#ifndef _NET_IF_LLC_H_ +#define _NET_IF_LLC_H_ + +/* + * IEEE 802.2 Link Level Control headers, for use in conjunction with + * 802.{3,4,5} media access control methods. + * + * Headers here do not use bit fields due to shortcommings in many + * compilers. + */ + +struct llc { + uint8_t llc_dsap; + uint8_t llc_ssap; + union { + struct { + uint8_t control; + uint8_t format_id; + uint8_t class; + uint8_t window_x2; + } __packed type_u; + struct { + uint8_t num_snd_x2; + uint8_t num_rcv_x2; + } __packed type_i; + struct { + uint8_t control; + uint8_t num_rcv_x2; + } __packed type_s; + struct { + uint8_t control; + /* + * We cannot put the following fields in a structure because + * the structure rounding might cause padding. + */ + uint8_t frmr_rej_pdu0; + uint8_t frmr_rej_pdu1; + uint8_t frmr_control; + uint8_t frmr_control_ext; + uint8_t frmr_cause; + } __packed type_frmr; + struct { + uint8_t control; + uint8_t org_code[3]; + uint16_t ether_type; + } __packed type_snap; + struct { + uint8_t control; + uint8_t control_ext; + } __packed type_raw; + } __packed llc_un; +} __packed; + +struct frmrinfo { + uint8_t frmr_rej_pdu0; + uint8_t frmr_rej_pdu1; + uint8_t frmr_control; + uint8_t frmr_control_ext; + uint8_t frmr_cause; +} __packed; + +#define llc_control llc_un.type_u.control +#define llc_control_ext llc_un.type_raw.control_ext +#define llc_fid llc_un.type_u.format_id +#define llc_class llc_un.type_u.class +#define llc_window llc_un.type_u.window_x2 +#define llc_frmrinfo llc_un.type_frmr.frmr_rej_pdu0 +#define llc_frmr_pdu0 llc_un.type_frmr.frmr_rej_pdu0 +#define llc_frmr_pdu1 llc_un.type_frmr.frmr_rej_pdu1 +#define llc_frmr_control llc_un.type_frmr.frmr_control +#define llc_frmr_control_ext llc_un.type_frmr.frmr_control_ext +#define llc_frmr_cause llc_un.type_frmr.frmr_cause +#define llc_snap llc_un.type_snap + +/* + * Don't use sizeof(struct llc_un) for LLC header sizes + */ +#define LLC_ISFRAMELEN 4 +#define LLC_UFRAMELEN 3 +#define LLC_FRMRLEN 7 +#define LLC_SNAPFRAMELEN 8 + +#ifdef CTASSERT +CTASSERT(sizeof (struct llc) == LLC_SNAPFRAMELEN); +#endif + +/* + * Unnumbered LLC format commands + */ +#define LLC_UI 0x3 +#define LLC_UI_P 0x13 +#define LLC_DISC 0x43 +#define LLC_DISC_P 0x53 +#define LLC_UA 0x63 +#define LLC_UA_P 0x73 +#define LLC_TEST 0xe3 +#define LLC_TEST_P 0xf3 +#define LLC_FRMR 0x87 +#define LLC_FRMR_P 0x97 +#define LLC_DM 0x0f +#define LLC_DM_P 0x1f +#define LLC_XID 0xaf +#define LLC_XID_P 0xbf +#define LLC_SABME 0x6f +#define LLC_SABME_P 0x7f + +/* + * Supervisory LLC commands + */ +#define LLC_RR 0x01 +#define LLC_RNR 0x05 +#define LLC_REJ 0x09 + +/* + * Info format - dummy only + */ +#define LLC_INFO 0x00 + +/* + * ISO PDTR 10178 contains among others + */ +#define LLC_8021D_LSAP 0x42 +#define LLC_X25_LSAP 0x7e +#define LLC_SNAP_LSAP 0xaa +#define LLC_ISO_LSAP 0xfe + +#define RFC1042_LEN 6 +#define RFC1042 {0xAA, 0xAA, 0x03, 0x00, 0x00, 0x00} +#define ETHERNET_TUNNEL {0xAA, 0xAA, 0x03, 0x00, 0x00, 0xF8} + +/* + * copied from sys/net/ethernet.h + */ +#define ETHERTYPE_AARP 0x80F3 /* AppleTalk AARP */ +#define ETHERTYPE_IPX 0x8137 /* Novell (old) NetWare IPX (ECONFIG E option) */ + + + +#endif /* _NET_IF_LLC_H_ */ diff --git a/app/include/netif/ppp_oe.h b/app/include/netif/ppp_oe.h new file mode 100644 index 00000000..e1cdfa51 --- /dev/null +++ b/app/include/netif/ppp_oe.h @@ -0,0 +1,190 @@ +/***************************************************************************** +* ppp_oe.h - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PPP_OE_H +#define PPP_OE_H + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT > 0 + +#include "netif/etharp.h" + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoehdr { + PACK_STRUCT_FIELD(u8_t vertype); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t session); + PACK_STRUCT_FIELD(u16_t plen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoetag { + PACK_STRUCT_FIELD(u16_t tag); + PACK_STRUCT_FIELD(u16_t len); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#define PPPOE_STATE_INITIAL 0 +#define PPPOE_STATE_PADI_SENT 1 +#define PPPOE_STATE_PADR_SENT 2 +#define PPPOE_STATE_SESSION 3 +#define PPPOE_STATE_CLOSING 4 +/* passive */ +#define PPPOE_STATE_PADO_SENT 1 + +#define PPPOE_HEADERLEN sizeof(struct pppoehdr) +#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */ + +#define PPPOE_TAG_EOL 0x0000 /* end of list */ +#define PPPOE_TAG_SNAME 0x0101 /* service name */ +#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */ +#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */ +#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */ +#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */ +#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */ +#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */ +#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */ +#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */ + +#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */ +#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */ +#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */ +#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */ +#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */ + +#ifndef ETHERMTU +#define ETHERMTU 1500 +#endif + +/* two byte PPP protocol discriminator, then IP data */ +#define PPPOE_MAXMTU (ETHERMTU-PPPOE_HEADERLEN-2) + +#ifndef PPPOE_MAX_AC_COOKIE_LEN +#define PPPOE_MAX_AC_COOKIE_LEN 64 +#endif + +struct pppoe_softc { + struct pppoe_softc *next; + struct netif *sc_ethif; /* ethernet interface we are using */ + int sc_pd; /* ppp unit number */ + void (*sc_linkStatusCB)(int pd, int up); + + int sc_state; /* discovery phase or session connected */ + struct eth_addr sc_dest; /* hardware address of concentrator */ + u16_t sc_session; /* PPPoE session id */ + +#ifdef PPPOE_TODO + char *sc_service_name; /* if != NULL: requested name of service */ + char *sc_concentrator_name; /* if != NULL: requested concentrator id */ +#endif /* PPPOE_TODO */ + u8_t sc_ac_cookie[PPPOE_MAX_AC_COOKIE_LEN]; /* content of AC cookie we must echo back */ + size_t sc_ac_cookie_len; /* length of cookie data */ +#ifdef PPPOE_SERVER + u8_t *sc_hunique; /* content of host unique we must echo back */ + size_t sc_hunique_len; /* length of host unique */ +#endif + int sc_padi_retried; /* number of PADI retries already done */ + int sc_padr_retried; /* number of PADR retries already done */ +}; + + +#define pppoe_init() /* compatibility define, no initialization needed */ + +err_t pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr); +err_t pppoe_destroy(struct netif *ifp); + +int pppoe_connect(struct pppoe_softc *sc); +void pppoe_disconnect(struct pppoe_softc *sc); + +void pppoe_disc_input(struct netif *netif, struct pbuf *p); +void pppoe_data_input(struct netif *netif, struct pbuf *p); + +err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb); + +/** used in ppp.c */ +#define PPPOE_HDRLEN (sizeof(struct eth_hdr) + PPPOE_HEADERLEN) + +#endif /* PPPOE_SUPPORT */ + +#endif /* PPP_OE_H */ diff --git a/app/include/netif/wlan_lwip_if.h b/app/include/netif/wlan_lwip_if.h new file mode 100644 index 00000000..ed9c4775 --- /dev/null +++ b/app/include/netif/wlan_lwip_if.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2011 Espressif System + * +*/ + +#ifndef _WLAN_LWIP_IF_H_ +#define _WLAN_LWIP_IF_H_ + +#define LWIP_IF0_PRIO 28 +#define LWIP_IF1_PRIO 29 + +enum { + SIG_LWIP_RX = 0, +}; + +struct netif * eagle_lwip_if_alloc(struct ieee80211_conn *conn, const uint8 *macaddr, struct ip_info *info); +struct netif * eagle_lwip_getif(uint8 index); + +#ifndef IOT_SIP_MODE +sint8 ieee80211_output_pbuf(struct netif *ifp, struct pbuf* pb); +#else +sint8 ieee80211_output_pbuf(struct ieee80211_conn *conn, esf_buf *eb); +#endif + +#endif /* _WLAN_LWIP_IF_H_ */ diff --git a/app/include/pp/esf_buf.h b/app/include/pp/esf_buf.h new file mode 100644 index 00000000..01603852 --- /dev/null +++ b/app/include/pp/esf_buf.h @@ -0,0 +1,12 @@ +/* + * copyright (c) 2008 - 2012 Espressif System + * + * esf buffer data structure + */ + +#ifndef _ESF_BUF_H_ +#define _ESF_BUF_H_ + +#define EP_OFFSET 36 /* see comments in pbuf.h */ + +#endif /* _ESF_BUF_H_ */ diff --git a/app/include/ssl/app/espconn_secure.h b/app/include/ssl/app/espconn_secure.h new file mode 100644 index 00000000..0039b1ad --- /dev/null +++ b/app/include/ssl/app/espconn_secure.h @@ -0,0 +1,46 @@ +#ifndef __ESPCONN_ENCRY_H__ +#define __ESPCONN_ENCRY_H__ + +#include "espconn/espconn.h" + +/****************************************************************************** + * FunctionName : espconn_encry_connect + * Description : The function given as connection + * Parameters : espconn -- the espconn used to connect with the host + * Returns : none +*******************************************************************************/ + +sint8 espconn_secure_connect(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_encry_disconnect + * Description : The function given as the disconnection + * Parameters : espconn -- the espconn used to disconnect with the host + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_secure_disconnect(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_encry_sent + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_secure_sent(struct espconn *espconn, uint8 *psent, uint16 length); + +/****************************************************************************** + * FunctionName : espconn_secure_accept + * Description : The function given as the listen + * Parameters : espconn -- the espconn used to listen the connection + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_secure_accept(struct espconn *espconn); + +#endif + + diff --git a/app/include/ssl/app/espconn_ssl.h b/app/include/ssl/app/espconn_ssl.h new file mode 100644 index 00000000..6f16297e --- /dev/null +++ b/app/include/ssl/app/espconn_ssl.h @@ -0,0 +1,59 @@ +#ifndef ESPCONN_SSL_CLIENT_H +#define ESPCONN_SSL_CLIENT_H + +#include "ssl/ssl_ssl.h" +#include "ssl/ssl_tls1.h" + +#include "lwip/app/espconn.h" + +typedef struct _ssl_msg { + SSL_CTX *ssl_ctx; + SSL *ssl; + bool quiet; + char *private_key_file; + uint8_t session_id[SSL_SESSION_ID_SIZE]; + u16_t pkt_length; +} ssl_msg; + +/****************************************************************************** + * FunctionName : sslserver_start + * Description : Initialize the server: set up a listen PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build client + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_ssl_server(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_ssl_client + * Description : Initialize the client: set up a connect PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build client + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_ssl_client(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_ssl_write + * Description : sent data for client or server + * Parameters : void *arg -- client or server to send + * uint8* psent -- Data to send + * uint16 length -- Length of data to send + * Returns : none +*******************************************************************************/ + +extern void espconn_ssl_sent(void *arg, uint8 *psent, uint16 length); + +/****************************************************************************** + * FunctionName : espconn_ssl_disconnect + * Description : A new incoming connection has been disconnected. + * Parameters : espconn -- the espconn used to disconnect with host + * Returns : none +*******************************************************************************/ + +extern void espconn_ssl_disconnect(espconn_msg *pdis); + +#endif + diff --git a/app/include/ssl/cert.h b/app/include/ssl/cert.h new file mode 100644 index 00000000..1e9e85c9 --- /dev/null +++ b/app/include/ssl/cert.h @@ -0,0 +1,36 @@ +unsigned char default_certificate[] = { + 0x30, 0x82, 0x01, 0x82, 0x30, 0x81, 0xec, 0x02, 0x09, 0x00, 0x88, 0xf2, + 0x5f, 0x46, 0x12, 0x2e, 0x3d, 0x3a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x1c, 0x31, + 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x77, 0x77, + 0x77, 0x2e, 0x65, 0x73, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x66, 0x2e, + 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x36, 0x32, + 0x34, 0x31, 0x30, 0x32, 0x32, 0x33, 0x33, 0x5a, 0x17, 0x0d, 0x32, 0x38, + 0x30, 0x33, 0x30, 0x32, 0x31, 0x30, 0x32, 0x32, 0x33, 0x33, 0x5a, 0x30, + 0x34, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, + 0x65, 0x73, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x66, 0x31, 0x1e, 0x30, + 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x15, 0x65, 0x73, 0x70, 0x72, + 0x65, 0x73, 0x73, 0x69, 0x66, 0x20, 0x49, 0x6f, 0x54, 0x20, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, + 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0xb9, 0x83, 0x30, 0xca, 0xfb, 0xec, + 0x11, 0x9e, 0x94, 0xb7, 0x89, 0xf2, 0x84, 0x2c, 0xda, 0xe1, 0x9a, 0x53, + 0x3a, 0x1b, 0x6e, 0xc9, 0x85, 0x81, 0xf9, 0xa3, 0x41, 0xdb, 0xe2, 0x82, + 0x3b, 0xfa, 0x80, 0x22, 0x3b, 0x81, 0x6d, 0x25, 0x73, 0x7e, 0xf6, 0x49, + 0xcc, 0x69, 0x3c, 0x6c, 0xd8, 0x05, 0xfb, 0x92, 0x02, 0xcf, 0x19, 0x2a, + 0x10, 0x7d, 0x69, 0x7a, 0xd8, 0x9d, 0xd3, 0xcf, 0x6c, 0xef, 0x02, 0x03, + 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x2d, 0x63, + 0x58, 0x21, 0xe3, 0x8b, 0x37, 0x0d, 0x28, 0x68, 0x11, 0x0e, 0x4d, 0xdd, + 0xf3, 0xea, 0xdb, 0xec, 0xd7, 0x09, 0x47, 0x2c, 0xa1, 0xd8, 0xd1, 0x71, + 0x83, 0x11, 0xb4, 0x17, 0xbc, 0x83, 0xea, 0x5a, 0xd6, 0x73, 0x02, 0x25, + 0x87, 0x01, 0x76, 0xfc, 0x59, 0x1a, 0xcf, 0xd9, 0x49, 0xc9, 0xf9, 0x1f, + 0x5c, 0x3b, 0x24, 0x6a, 0x5c, 0xa5, 0xca, 0xe6, 0x5d, 0x34, 0x5b, 0x5f, + 0xcf, 0x56, 0x9c, 0x71, 0xd2, 0x6b, 0xdd, 0x1f, 0x15, 0xae, 0x4d, 0xf1, + 0xca, 0x35, 0xc8, 0xdd, 0x93, 0x1b, 0x58, 0x1e, 0x94, 0x08, 0xcf, 0xa0, + 0x20, 0xb9, 0x75, 0xa5, 0x4c, 0x77, 0xf5, 0x7f, 0xed, 0xd5, 0xcd, 0x53, + 0xaa, 0x87, 0xa6, 0x3c, 0xf5, 0x72, 0xd8, 0xd2, 0xb0, 0xf7, 0x11, 0xb0, + 0x0e, 0xe9, 0x41, 0xd6, 0x8e, 0xd9, 0x07, 0xf8, 0xed, 0xf8, 0x67, 0x7f, + 0x28, 0x18, 0xf0, 0x1b, 0x29, 0x11 +}; +unsigned int default_certificate_len = 390; diff --git a/app/include/ssl/private_key.h b/app/include/ssl/private_key.h new file mode 100644 index 00000000..7e8783dd --- /dev/null +++ b/app/include/ssl/private_key.h @@ -0,0 +1,30 @@ +unsigned char default_private_key[] = { + 0x30, 0x82, 0x01, 0x3a, 0x02, 0x01, 0x00, 0x02, 0x41, 0x00, 0xb9, 0x83, + 0x30, 0xca, 0xfb, 0xec, 0x11, 0x9e, 0x94, 0xb7, 0x89, 0xf2, 0x84, 0x2c, + 0xda, 0xe1, 0x9a, 0x53, 0x3a, 0x1b, 0x6e, 0xc9, 0x85, 0x81, 0xf9, 0xa3, + 0x41, 0xdb, 0xe2, 0x82, 0x3b, 0xfa, 0x80, 0x22, 0x3b, 0x81, 0x6d, 0x25, + 0x73, 0x7e, 0xf6, 0x49, 0xcc, 0x69, 0x3c, 0x6c, 0xd8, 0x05, 0xfb, 0x92, + 0x02, 0xcf, 0x19, 0x2a, 0x10, 0x7d, 0x69, 0x7a, 0xd8, 0x9d, 0xd3, 0xcf, + 0x6c, 0xef, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x40, 0x1d, 0x13, 0x92, + 0xf2, 0x3d, 0xca, 0x22, 0x78, 0xd8, 0x96, 0x6b, 0xe8, 0xb7, 0x0e, 0xd0, + 0xbf, 0xcb, 0x90, 0x7f, 0xeb, 0x0c, 0xd2, 0x49, 0x3a, 0xb6, 0x06, 0x00, + 0xac, 0x96, 0x34, 0x13, 0x72, 0x4b, 0x8c, 0xd2, 0xb9, 0x35, 0xf5, 0x64, + 0x18, 0xb2, 0x47, 0x5b, 0x9f, 0xbb, 0xf2, 0x5b, 0x2f, 0x66, 0x78, 0x2d, + 0x0a, 0x76, 0x44, 0xc5, 0x4f, 0xdb, 0x7d, 0x13, 0xcf, 0xa5, 0x08, 0xdc, + 0x01, 0x02, 0x21, 0x00, 0xdf, 0x9a, 0x89, 0xd0, 0xef, 0x23, 0xcf, 0x12, + 0xac, 0x8a, 0x63, 0x1a, 0x8c, 0xc0, 0x3f, 0xf4, 0x38, 0x52, 0x3c, 0x9f, + 0x19, 0x0a, 0x37, 0xd2, 0xcb, 0x5d, 0xeb, 0xb6, 0x2a, 0x33, 0xb0, 0x91, + 0x02, 0x21, 0x00, 0xd4, 0x63, 0xd9, 0x6a, 0x18, 0x5b, 0xe8, 0xa8, 0x57, + 0x4d, 0xd1, 0x9a, 0xa8, 0xd7, 0xe1, 0x65, 0x75, 0xb3, 0xb9, 0x5c, 0x94, + 0x14, 0xca, 0x98, 0x41, 0x47, 0x9c, 0x0a, 0x22, 0x38, 0x05, 0x7f, 0x02, + 0x20, 0x6a, 0xce, 0xfd, 0xef, 0xe0, 0x9b, 0x61, 0x49, 0x91, 0x43, 0x95, + 0x6d, 0x54, 0x38, 0x6d, 0x14, 0x32, 0x67, 0x0d, 0xf0, 0x0d, 0x5c, 0xf5, + 0x27, 0x6a, 0xdf, 0x55, 0x3d, 0xb1, 0xd0, 0xf9, 0x11, 0x02, 0x21, 0x00, + 0xba, 0x94, 0xa0, 0xf9, 0xb0, 0x3e, 0x85, 0x8b, 0xe5, 0x6e, 0x4a, 0x95, + 0x88, 0x80, 0x65, 0xd5, 0x00, 0xea, 0x8b, 0x0b, 0x46, 0x57, 0x61, 0x87, + 0x11, 0xc9, 0xfb, 0xcd, 0x77, 0x34, 0x29, 0xb7, 0x02, 0x20, 0x06, 0x8d, + 0x41, 0x11, 0x47, 0x93, 0xcb, 0xad, 0xda, 0x5d, 0xe1, 0x9d, 0x49, 0x8d, + 0xe0, 0xab, 0x48, 0xe6, 0x18, 0x28, 0x4a, 0x94, 0xae, 0xf9, 0xad, 0xc5, + 0x5b, 0x0b, 0x15, 0xc6, 0x73, 0x17 +}; +unsigned int default_private_key_len = 318; diff --git a/app/include/ssl/ssl_bigint.h b/app/include/ssl/ssl_bigint.h new file mode 100644 index 00000000..99f54151 --- /dev/null +++ b/app/include/ssl/ssl_bigint.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BIGINT_HEADER +#define BIGINT_HEADER + +#include "ssl/ssl_crypto.h" + +BI_CTX *bi_initialize(void); +void bi_terminate(BI_CTX *ctx); +void bi_permanent(bigint *bi); +void bi_depermanent(bigint *bi); +void bi_clear_cache(BI_CTX *ctx); +void bi_free(BI_CTX *ctx, bigint *bi); +bigint *bi_copy(bigint *bi); +bigint *bi_clone(BI_CTX *ctx, const bigint *bi); +void bi_export(BI_CTX *ctx, bigint *bi, uint8_t *data, int size); +bigint *bi_import(BI_CTX *ctx, const uint8_t *data, int len); +bigint *int_to_bi(BI_CTX *ctx, comp i); + +/* the functions that actually do something interesting */ +bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib); +bigint *bi_subtract(BI_CTX *ctx, bigint *bia, + bigint *bib, int *is_negative); +bigint *bi_divide(BI_CTX *ctx, bigint *bia, bigint *bim, int is_mod); +bigint *bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib); +bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp); +bigint *bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp); +int bi_compare(bigint *bia, bigint *bib); +void bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset); +void bi_free_mod(BI_CTX *ctx, int mod_offset); + +#ifdef CONFIG_SSL_FULL_MODE +void bi_print(const char *label, bigint *bi); +bigint *bi_str_import(BI_CTX *ctx, const char *data); +#endif + +/** + * @def bi_mod + * Find the residue of B. bi_set_mod() must be called before hand. + */ +#define bi_mod(A, B) bi_divide(A, B, ctx->bi_mod[ctx->mod_offset], 1) + +/** + * bi_residue() is technically the same as bi_mod(), but it uses the + * appropriate reduction technique (which is bi_mod() when doing classical + * reduction). + */ +#if defined(CONFIG_BIGINT_MONTGOMERY) +#define bi_residue(A, B) bi_mont(A, B) +bigint *bi_mont(BI_CTX *ctx, bigint *bixy); +#elif defined(CONFIG_BIGINT_BARRETT) +#define bi_residue(A, B) bi_barrett(A, B) +bigint *bi_barrett(BI_CTX *ctx, bigint *bi); +#else /* if defined(CONFIG_BIGINT_CLASSICAL) */ +#define bi_residue(A, B) bi_mod(A, B) +#endif + +#ifdef CONFIG_BIGINT_SQUARE +bigint *bi_square(BI_CTX *ctx, bigint *bi); +#else +#define bi_square(A, B) bi_multiply(A, bi_copy(B), B) +#endif + +#ifdef CONFIG_BIGINT_CRT +bigint *bi_crt(BI_CTX *ctx, bigint *bi, + bigint *dP, bigint *dQ, + bigint *p, bigint *q, + bigint *qInv); +#endif + +#endif diff --git a/app/include/ssl/ssl_bigint_impl.h b/app/include/ssl/ssl_bigint_impl.h new file mode 100644 index 00000000..c82fefb6 --- /dev/null +++ b/app/include/ssl/ssl_bigint_impl.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BIGINT_IMPL_HEADER +#define BIGINT_IMPL_HEADER + +/* Maintain a number of precomputed variables when doing reduction */ +#define BIGINT_M_OFFSET 0 /**< Normal modulo offset. */ +#ifdef CONFIG_BIGINT_CRT +#define BIGINT_P_OFFSET 1 /**< p modulo offset. */ +#define BIGINT_Q_OFFSET 2 /**< q module offset. */ +#define BIGINT_NUM_MODS 3 /**< The number of modulus constants used. */ +#else +#define BIGINT_NUM_MODS 1 +#endif + +/* Architecture specific functions for big ints */ +#if defined(CONFIG_INTEGER_8BIT) +#define COMP_RADIX 256U /**< Max component + 1 */ +#define COMP_MAX 0xFFFFU/**< (Max dbl comp -1) */ +#define COMP_BIT_SIZE 8 /**< Number of bits in a component. */ +#define COMP_BYTE_SIZE 1 /**< Number of bytes in a component. */ +#define COMP_NUM_NIBBLES 2 /**< Used For diagnostics only. */ +typedef uint8_t comp; /**< A single precision component. */ +typedef uint16_t long_comp; /**< A double precision component. */ +typedef int16_t slong_comp; /**< A signed double precision component. */ +#elif defined(CONFIG_INTEGER_16BIT) +#define COMP_RADIX 65536U /**< Max component + 1 */ +#define COMP_MAX 0xFFFFFFFFU/**< (Max dbl comp -1) */ +#define COMP_BIT_SIZE 16 /**< Number of bits in a component. */ +#define COMP_BYTE_SIZE 2 /**< Number of bytes in a component. */ +#define COMP_NUM_NIBBLES 4 /**< Used For diagnostics only. */ +typedef uint16_t comp; /**< A single precision component. */ +typedef uint32_t long_comp; /**< A double precision component. */ +typedef int32_t slong_comp; /**< A signed double precision component. */ +#else /* regular 32 bit */ +#ifdef WIN32 +#define COMP_RADIX 4294967296i64 +#define COMP_MAX 0xFFFFFFFFFFFFFFFFui64 +#else +#define COMP_RADIX 4294967296ULL /**< Max component + 1 */ +#define COMP_MAX 0xFFFFFFFFFFFFFFFFULL/**< (Max dbl comp -1) */ +#endif +#define COMP_BIT_SIZE 32 /**< Number of bits in a component. */ +#define COMP_BYTE_SIZE 4 /**< Number of bytes in a component. */ +#define COMP_NUM_NIBBLES 8 /**< Used For diagnostics only. */ +typedef uint32_t comp; /**< A single precision component. */ +typedef uint64_t long_comp; /**< A double precision component. */ +typedef sint64_t slong_comp; /**< A signed double precision component. */ +#endif + +/** + * @struct _bigint + * @brief A big integer basic object + */ +struct _bigint +{ + struct _bigint* next; /**< The next bigint in the cache. */ + short size; /**< The number of components in this bigint. */ + short max_comps; /**< The heapsize allocated for this bigint */ + int refs; /**< An internal reference count. */ + comp* comps; /**< A ptr to the actual component data */ +}; + +typedef struct _bigint bigint; /**< An alias for _bigint */ + +/** + * Maintains the state of the cache, and a number of variables used in + * reduction. + */ +typedef struct /**< A big integer "session" context. */ +{ + bigint *active_list; /**< Bigints currently used. */ + bigint *free_list; /**< Bigints not used. */ + bigint *bi_radix; /**< The radix used. */ + bigint *bi_mod[BIGINT_NUM_MODS]; /**< modulus */ + +#if defined(CONFIG_BIGINT_MONTGOMERY) + bigint *bi_RR_mod_m[BIGINT_NUM_MODS]; /**< R^2 mod m */ + bigint *bi_R_mod_m[BIGINT_NUM_MODS]; /**< R mod m */ + comp N0_dash[BIGINT_NUM_MODS]; +#elif defined(CONFIG_BIGINT_BARRETT) + bigint *bi_mu[BIGINT_NUM_MODS]; /**< Storage for mu */ +#endif + bigint *bi_normalised_mod[BIGINT_NUM_MODS]; /**< Normalised mod storage. */ + bigint **g; /**< Used by sliding-window. */ + int window; /**< The size of the sliding window */ + int active_count; /**< Number of active bigints. */ + int free_count; /**< Number of free bigints. */ + +#ifdef CONFIG_BIGINT_MONTGOMERY + uint8_t use_classical; /**< Use classical reduction. */ +#endif + uint8_t mod_offset; /**< The mod offset we are using */ +} BI_CTX; + +#ifndef WIN32 +#define max(a,b) ((a)>(b)?(a):(b)) /**< Find the maximum of 2 numbers. */ +#define min(a,b) ((a)<(b)?(a):(b)) /**< Find the minimum of 2 numbers. */ +#endif + +#define PERMANENT 0x7FFF55AA /**< A magic number for permanents. */ + +#endif diff --git a/app/include/ssl/ssl_cert.h b/app/include/ssl/ssl_cert.h new file mode 100644 index 00000000..30c7b658 --- /dev/null +++ b/app/include/ssl/ssl_cert.h @@ -0,0 +1,43 @@ +unsigned char default_certificate[] = { + 0x30, 0x82, 0x01, 0xd7, 0x30, 0x82, 0x01, 0x40, 0x02, 0x09, 0x00, 0xab, + 0x08, 0x18, 0xa7, 0x03, 0x07, 0x27, 0xfd, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x34, + 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x29, 0x61, + 0x78, 0x54, 0x4c, 0x53, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x20, 0x44, 0x6f, 0x64, 0x67, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x31, 0x32, + 0x32, 0x36, 0x32, 0x32, 0x33, 0x33, 0x33, 0x39, 0x5a, 0x17, 0x0d, 0x32, + 0x34, 0x30, 0x39, 0x30, 0x33, 0x32, 0x32, 0x33, 0x33, 0x33, 0x39, 0x5a, + 0x30, 0x2c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x61, 0x78, 0x54, 0x4c, 0x53, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, + 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, + 0x81, 0x81, 0x00, 0xcd, 0xfd, 0x89, 0x48, 0xbe, 0x36, 0xb9, 0x95, 0x76, + 0xd4, 0x13, 0x30, 0x0e, 0xbf, 0xb2, 0xed, 0x67, 0x0a, 0xc0, 0x16, 0x3f, + 0x51, 0x09, 0x9d, 0x29, 0x2f, 0xb2, 0x6d, 0x3f, 0x3e, 0x6c, 0x2f, 0x90, + 0x80, 0xa1, 0x71, 0xdf, 0xbe, 0x38, 0xc5, 0xcb, 0xa9, 0x9a, 0x40, 0x14, + 0x90, 0x0a, 0xf9, 0xb7, 0x07, 0x0b, 0xe1, 0xda, 0xe7, 0x09, 0xbf, 0x0d, + 0x57, 0x41, 0x86, 0x60, 0xa1, 0xc1, 0x27, 0x91, 0x5b, 0x0a, 0x98, 0x46, + 0x1b, 0xf6, 0xa2, 0x84, 0xf8, 0x65, 0xc7, 0xce, 0x2d, 0x96, 0x17, 0xaa, + 0x91, 0xf8, 0x61, 0x04, 0x50, 0x70, 0xeb, 0xb4, 0x43, 0xb7, 0xdc, 0x9a, + 0xcc, 0x31, 0x01, 0x14, 0xd4, 0xcd, 0xcc, 0xc2, 0x37, 0x6d, 0x69, 0x82, + 0xd6, 0xc6, 0xc4, 0xbe, 0xf2, 0x34, 0xa5, 0xc9, 0xa6, 0x19, 0x53, 0x32, + 0x7a, 0x86, 0x0e, 0x91, 0x82, 0x0f, 0xa1, 0x42, 0x54, 0xaa, 0x01, 0x02, + 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x40, + 0xb4, 0x94, 0x9a, 0xa8, 0x89, 0x72, 0x1d, 0x07, 0xe5, 0xb3, 0x6b, 0x88, + 0x21, 0xc2, 0x38, 0x36, 0x9e, 0x7a, 0x8c, 0x49, 0x48, 0x68, 0x0c, 0x06, + 0xe8, 0xdb, 0x1f, 0x4e, 0x05, 0xe6, 0x31, 0xe3, 0xfd, 0xe6, 0x0d, 0x6b, + 0xd8, 0x13, 0x17, 0xe0, 0x2d, 0x0d, 0xb8, 0x7e, 0xcb, 0x20, 0x6c, 0xa8, + 0x73, 0xa7, 0xfd, 0xe3, 0xa7, 0xfa, 0xf3, 0x02, 0x60, 0x78, 0x1f, 0x13, + 0x40, 0x45, 0xee, 0x75, 0xf5, 0x10, 0xfd, 0x8f, 0x68, 0x74, 0xd4, 0xac, + 0xae, 0x04, 0x09, 0x55, 0x2c, 0xdb, 0xd8, 0x07, 0x07, 0x65, 0x69, 0x27, + 0x6e, 0xbf, 0x5e, 0x61, 0x40, 0x56, 0x8b, 0xd7, 0x33, 0x3b, 0xff, 0x6e, + 0x53, 0x7e, 0x9d, 0x3f, 0xc0, 0x40, 0x3a, 0xab, 0xa0, 0x50, 0x4e, 0x80, + 0x47, 0x46, 0x0d, 0x1e, 0xdb, 0x4c, 0xf1, 0x1b, 0x5d, 0x3c, 0x2a, 0x54, + 0xa7, 0x4d, 0xfa, 0x7b, 0x72, 0x66, 0xc5 +}; +unsigned int default_certificate_len = 475; diff --git a/app/include/ssl/ssl_config.h b/app/include/ssl/ssl_config.h new file mode 100644 index 00000000..3404b5be --- /dev/null +++ b/app/include/ssl/ssl_config.h @@ -0,0 +1,127 @@ +/* + * Automatically generated header file: don't edit + */ + +#define HAVE_DOT_CONFIG 1 +#undef CONFIG_PLATFORM_LINUX +#define CONFIG_PLATFORM_CYGWIN 1 +#undef CONFIG_PLATFORM_WIN32 + +/* + * General Configuration + */ +#define PREFIX "/usr/local" +#define CONFIG_DEBUG 1 +#undef CONFIG_STRIP_UNWANTED_SECTIONS +#undef CONFIG_VISUAL_STUDIO_7_0 +#undef CONFIG_VISUAL_STUDIO_8_0 +#undef CONFIG_VISUAL_STUDIO_10_0 +#define CONFIG_VISUAL_STUDIO_7_0_BASE "" +#define CONFIG_VISUAL_STUDIO_8_0_BASE "" +#define CONFIG_VISUAL_STUDIO_10_0_BASE "" +#define CONFIG_EXTRA_CFLAGS_OPTIONS "" +#define CONFIG_EXTRA_LDFLAGS_OPTIONS "" + +/* + * SSL Library + */ +#undef CONFIG_SSL_SERVER_ONLY +#undef CONFIG_SSL_CERT_VERIFICATION +#undef CONFIG_SSL_ENABLE_CLIENT +#define CONFIG_SSL_FULL_MODE 1 +#undef CONFIG_SSL_SKELETON_MODE +#undef CONFIG_SSL_PROT_LOW +#define CONFIG_SSL_PROT_MEDIUM 1 +#undef CONFIG_SSL_PROT_HIGH +#define CONFIG_SSL_USE_DEFAULT_KEY +#define CONFIG_SSL_PRIVATE_KEY_LOCATION "" +#define CONFIG_SSL_PRIVATE_KEY_PASSWORD "" +#define CONFIG_SSL_X509_CERT_LOCATION "" +#undef CONFIG_SSL_GENERATE_X509_CERT +#define CONFIG_SSL_X509_COMMON_NAME "" +#define CONFIG_SSL_X509_ORGANIZATION_NAME "" +#define CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME "" +#undef CONFIG_SSL_ENABLE_V23_HANDSHAKE +#define CONFIG_SSL_HAS_PEM 1 +#undef CONFIG_SSL_USE_PKCS12 +#define CONFIG_SSL_EXPIRY_TIME 24 +#define CONFIG_X509_MAX_CA_CERTS 150 +#define CONFIG_SSL_MAX_CERTS 3 +#undef CONFIG_SSL_CTX_MUTEXING +//#define CONFIG_USE_DEV_URANDOM 1 +#undef CONFIG_WIN32_USE_CRYPTO_LIB +#undef CONFIG_OPENSSL_COMPATIBLE +#undef CONFIG_PERFORMANCE_TESTING +#define CONFIG_SSL_TEST 1 +#undef CONFIG_AXTLSWRAP +#define CONFIG_AXHTTPD 1 + +/* + * Axhttpd Configuration + */ +#undef CONFIG_HTTP_STATIC_BUILD +#define CONFIG_HTTP_PORT 80 +#define CONFIG_HTTP_HTTPS_PORT 443 +#define CONFIG_HTTP_SESSION_CACHE_SIZE 5 +#define CONFIG_HTTP_WEBROOT "../www" +#define CONFIG_HTTP_TIMEOUT 300 + +/* + * CGI + */ +#undef CONFIG_HTTP_HAS_CGI +#define CONFIG_HTTP_CGI_EXTENSIONS ".lua,.lp,.php" +#define CONFIG_HTTP_ENABLE_LUA 1 +#define CONFIG_HTTP_LUA_PREFIX "/usr" +#undef CONFIG_HTTP_BUILD_LUA +#define CONFIG_HTTP_CGI_LAUNCHER "/usr/bin/cgi" +#define CONFIG_HTTP_DIRECTORIES 1 +#define CONFIG_HTTP_HAS_AUTHORIZATION 1 +#undef CONFIG_HTTP_HAS_IPV6 +#undef CONFIG_HTTP_ENABLE_DIFFERENT_USER +#define CONFIG_HTTP_USER "" +#define CONFIG_HTTP_VERBOSE 0 +#undef CONFIG_HTTP_IS_DAEMON + +/* + * Language Bindings + */ +#undef CONFIG_BINDINGS +#undef CONFIG_CSHARP_BINDINGS +#undef CONFIG_VBNET_BINDINGS +#define CONFIG_DOT_NET_FRAMEWORK_BASE "" +#undef CONFIG_JAVA_BINDINGS +#define CONFIG_JAVA_HOME "" +#undef CONFIG_PERL_BINDINGS +#define CONFIG_PERL_CORE "" +#define CONFIG_PERL_LIB "" +#undef CONFIG_LUA_BINDINGS +#define CONFIG_LUA_CORE "" + +/* + * Samples + */ +#define CONFIG_SAMPLES 1 +#define CONFIG_C_SAMPLES 1 +#undef CONFIG_CSHARP_SAMPLES +#undef CONFIG_VBNET_SAMPLES +#undef CONFIG_JAVA_SAMPLES +#undef CONFIG_PERL_SAMPLES +#undef CONFIG_LUA_SAMPLES + +/* + * BigInt Options + */ +#undef CONFIG_BIGINT_CLASSICAL +#undef CONFIG_BIGINT_MONTGOMERY +#define CONFIG_BIGINT_BARRETT 1 +#define CONFIG_BIGINT_CRT 1 +#undef CONFIG_BIGINT_KARATSUBA +#define MUL_KARATSUBA_THRESH +#define SQU_KARATSUBA_THRESH +#define CONFIG_BIGINT_SLIDING_WINDOW 1 +#define CONFIG_BIGINT_SQUARE 1 +#define CONFIG_BIGINT_CHECK_ON 1 +#define CONFIG_INTEGER_32BIT 1 +#undef CONFIG_INTEGER_16BIT +#undef CONFIG_INTEGER_8BIT diff --git a/app/include/ssl/ssl_crypto.h b/app/include/ssl/ssl_crypto.h new file mode 100644 index 00000000..1ea461a5 --- /dev/null +++ b/app/include/ssl/ssl_crypto.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file crypto.h + */ + +#ifndef HEADER_CRYPTO_H +#define HEADER_CRYPTO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ssl/ssl_config.h" +#include "ssl/ssl_bigint_impl.h" +#include "ssl/ssl_bigint.h" + +#ifndef STDCALL +#define STDCALL +#endif +#ifndef EXP_FUNC +#define EXP_FUNC +#endif + + +/* enable features based on a 'super-set' capbaility. */ +#if defined(CONFIG_SSL_FULL_MODE) +#define CONFIG_SSL_ENABLE_CLIENT +#define CONFIG_SSL_CERT_VERIFICATION +#elif defined(CONFIG_SSL_ENABLE_CLIENT) +#define CONFIG_SSL_CERT_VERIFICATION +#endif + +/************************************************************************** + * AES declarations + **************************************************************************/ + +#define AES_MAXROUNDS 14 +#define AES_BLOCKSIZE 16 +#define AES_IV_SIZE 16 + +typedef struct aes_key_st +{ + uint16_t rounds; + uint16_t key_size; + uint32_t ks[(AES_MAXROUNDS+1)*8]; + uint8_t iv[AES_IV_SIZE]; +} AES_CTX; + +typedef enum +{ + AES_MODE_128, + AES_MODE_256 +} AES_MODE; + +void AES_set_key(AES_CTX *ctx, const uint8_t *key, + const uint8_t *iv, AES_MODE mode); +void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, + uint8_t *out, int length); +void AES_cbc_decrypt(AES_CTX *ks, const uint8_t *in, uint8_t *out, int length); +void AES_convert_key(AES_CTX *ctx); + +/************************************************************************** + * RC4 declarations + **************************************************************************/ + +typedef struct +{ + uint8_t x, y, m[256]; +} RC4_CTX; + +void RC4_setup(RC4_CTX *s, const uint8_t *key, int length); +void RC4_crypt(RC4_CTX *s, const uint8_t *msg, uint8_t *data, int length); + +/************************************************************************** + * SHA1 declarations + **************************************************************************/ + +#define SHA1_SIZE 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct +{ + uint32_t Intermediate_Hash[SHA1_SIZE/4]; /* Message Digest */ + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + uint16_t Message_Block_Index; /* Index into message block array */ + uint8_t Message_Block[64]; /* 512-bit message blocks */ +} SHA1_CTX; + +void SHA1_Init(SHA1_CTX *); +void SHA1_Update(SHA1_CTX *, const uint8_t * msg, int len); +void SHA1_Final(uint8_t *digest, SHA1_CTX *); + +/************************************************************************** + * MD2 declarations + **************************************************************************/ + +#define MD2_SIZE 16 + +typedef struct +{ + unsigned char cksum[16]; /* checksum of the data block */ + unsigned char state[48]; /* intermediate digest state */ + unsigned char buffer[16]; /* data block being processed */ + int left; /* amount of data in buffer */ +} MD2_CTX; + +EXP_FUNC void STDCALL MD2_Init(MD2_CTX *ctx); +EXP_FUNC void STDCALL MD2_Update(MD2_CTX *ctx, const uint8_t *input, int ilen); +EXP_FUNC void STDCALL MD2_Final(uint8_t *digest, MD2_CTX *ctx); + +/************************************************************************** + * MD5 declarations + **************************************************************************/ + +#define MD5_SIZE 16 + +typedef struct +{ + uint32_t state[4]; /* state (ABCD) */ + uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ + uint8_t buffer[64]; /* input buffer */ +} MD5_CTX; + +EXP_FUNC void STDCALL MD5_Init(MD5_CTX *); +EXP_FUNC void STDCALL MD5_Update(MD5_CTX *, const uint8_t *msg, int len); +EXP_FUNC void STDCALL MD5_Final(uint8_t *digest, MD5_CTX *); + +/************************************************************************** + * HMAC declarations + **************************************************************************/ +void ssl_hmac_md5(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest);// fix hmac_md5 to ssl_hmac_md5, discriminate ieee80211 +void ssl_hmac_sha1(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest);// fix hmac_md5 to ssl_hmac_sha1, discriminate ieee80211 + +/************************************************************************** + * RSA declarations + **************************************************************************/ + +typedef struct +{ + bigint *m; /* modulus */ + bigint *e; /* public exponent */ + bigint *d; /* private exponent */ +#ifdef CONFIG_BIGINT_CRT + bigint *p; /* p as in m = pq */ + bigint *q; /* q as in m = pq */ + bigint *dP; /* d mod (p-1) */ + bigint *dQ; /* d mod (q-1) */ + bigint *qInv; /* q^-1 mod p */ +#endif + int num_octets; + BI_CTX *bi_ctx; +} RSA_CTX; + +void RSA_priv_key_new(RSA_CTX **rsa_ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len, + const uint8_t *priv_exp, int priv_len +#ifdef CONFIG_BIGINT_CRT + , const uint8_t *p, int p_len, + const uint8_t *q, int q_len, + const uint8_t *dP, int dP_len, + const uint8_t *dQ, int dQ_len, + const uint8_t *qInv, int qInv_len +#endif + ); +void RSA_pub_key_new(RSA_CTX **rsa_ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len); +void RSA_free(RSA_CTX *ctx); +int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, + int is_decryption); +bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg); +#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT) +bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, + bigint *modulus, bigint *pub_exp); +bigint *RSA_public(const RSA_CTX * c, bigint *bi_msg); +int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, + uint8_t *out_data, int is_signing); +void RSA_print(const RSA_CTX *ctx); +#endif + +/************************************************************************** + * RNG declarations + **************************************************************************/ +EXP_FUNC void STDCALL RNG_initialize(void); +EXP_FUNC void STDCALL RNG_custom_init(const uint8_t *seed_buf, int size); +EXP_FUNC void STDCALL RNG_terminate(void); +EXP_FUNC void STDCALL get_random(int num_rand_bytes, uint8_t *rand_data); +void get_random_NZ(int num_rand_bytes, uint8_t *rand_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/include/ssl/ssl_crypto_misc.h b/app/include/ssl/ssl_crypto_misc.h new file mode 100644 index 00000000..8e1ba539 --- /dev/null +++ b/app/include/ssl/ssl_crypto_misc.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * @file crypto_misc.h + */ + +#ifndef HEADER_CRYPTO_MISC_H +#define HEADER_CRYPTO_MISC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ssl/ssl_crypto.h" +#include "ssl/ssl_bigint.h" + +/************************************************************************** + * X509 declarations + **************************************************************************/ +#define X509_OK 0 +#define X509_NOT_OK -1 +#define X509_VFY_ERROR_NO_TRUSTED_CERT -2 +#define X509_VFY_ERROR_BAD_SIGNATURE -3 +#define X509_VFY_ERROR_NOT_YET_VALID -4 +#define X509_VFY_ERROR_EXPIRED -5 +#define X509_VFY_ERROR_SELF_SIGNED -6 +#define X509_VFY_ERROR_INVALID_CHAIN -7 +#define X509_VFY_ERROR_UNSUPPORTED_DIGEST -8 +#define X509_INVALID_PRIV_KEY -9 + +/* + * The Distinguished Name + */ +#define X509_NUM_DN_TYPES 3 +#define X509_COMMON_NAME 0 +#define X509_ORGANIZATION 1 +#define X509_ORGANIZATIONAL_UNIT 2 + +struct _x509_ctx +{ + char *ca_cert_dn[X509_NUM_DN_TYPES]; + char *cert_dn[X509_NUM_DN_TYPES]; + char **subject_alt_dnsnames; + time_t not_before; + time_t not_after; + uint8_t *signature; + uint16_t sig_len; + uint8_t sig_type; + RSA_CTX *rsa_ctx; + bigint *digest; + struct _x509_ctx *next; +}; + +typedef struct _x509_ctx X509_CTX; + +#ifdef CONFIG_SSL_CERT_VERIFICATION +typedef struct +{ + X509_CTX *cert[CONFIG_X509_MAX_CA_CERTS]; +} CA_CERT_CTX; +#endif + +int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx); +void x509_free(X509_CTX *x509_ctx); +#ifdef CONFIG_SSL_CERT_VERIFICATION +int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert); +#endif +#ifdef CONFIG_SSL_FULL_MODE +void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx); +const char * x509_display_error(int error); +#endif + +/************************************************************************** + * ASN1 declarations + **************************************************************************/ +#define ASN1_INTEGER 0x02 +#define ASN1_BIT_STRING 0x03 +#define ASN1_OCTET_STRING 0x04 +#define ASN1_NULL 0x05 +#define ASN1_PRINTABLE_STR2 0x0C +#define ASN1_OID 0x06 +#define ASN1_PRINTABLE_STR2 0x0C +#define ASN1_PRINTABLE_STR 0x13 +#define ASN1_TELETEX_STR 0x14 +#define ASN1_IA5_STR 0x16 +#define ASN1_UTC_TIME 0x17 +#define ASN1_UNICODE_STR 0x1e +#define ASN1_SEQUENCE 0x30 +#define ASN1_CONTEXT_DNSNAME 0x82 +#define ASN1_SET 0x31 +#define ASN1_V3_DATA 0xa3 +#define ASN1_IMPLICIT_TAG 0x80 +#define ASN1_CONTEXT_DNSNAME 0x82 +#define ASN1_EXPLICIT_TAG 0xa0 +#define ASN1_V3_DATA 0xa3 + +#define SIG_TYPE_MD2 0x02 +#define SIG_TYPE_MD5 0x04 +#define SIG_TYPE_SHA1 0x05 + +int get_asn1_length(const uint8_t *buf, int *offset); +int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx); +int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type); +int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type); +int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object); +int asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +int asn1_name(const uint8_t *cert, int *offset, char *dn[]); +int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +#ifdef CONFIG_SSL_CERT_VERIFICATION +int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +int asn1_find_subjectaltname(const uint8_t* cert, int offset); +int asn1_compare_dn(char * const dn1[], char * const dn2[]); +#endif /* CONFIG_SSL_CERT_VERIFICATION */ +int asn1_signature_type(const uint8_t *cert, + int *offset, X509_CTX *x509_ctx); + +/************************************************************************** + * MISC declarations + **************************************************************************/ +#define SALT_SIZE 8 + +extern const char * const unsupported_str; + +typedef void (*crypt_func)(void *, const uint8_t *, uint8_t *, int); +typedef void (*hmac_func)(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest); + +int get_file(const char *filename, uint8_t **buf); + +#if defined(CONFIG_SSL_FULL_MODE) || defined(WIN32) || defined(CONFIG_DEBUG) +EXP_FUNC void STDCALL print_blob(const char *format, const uint8_t *data, int size, ...); +#else + #define print_blob(...) +#endif + +EXP_FUNC int STDCALL base64_decode(const char *in, int len, + uint8_t *out, int *outlen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/include/ssl/ssl_os_int.h b/app/include/ssl/ssl_os_int.h new file mode 100644 index 00000000..0d81bb28 --- /dev/null +++ b/app/include/ssl/ssl_os_int.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2012, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file os_int.h + * + * Ensure a consistent bit size + */ + +#ifndef HEADER_OS_INT_H +#define HEADER_OS_INT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(WIN32) +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; +typedef UINT64 uint64_t; +typedef INT64 int64_t; +#else /* Not Win32 */ + +#ifdef CONFIG_PLATFORM_SOLARIS +#include +#else +//#include +#endif /* Not Solaris */ + +#endif /* Not Win32 */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/include/ssl/ssl_os_port.h b/app/include/ssl/ssl_os_port.h new file mode 100644 index 00000000..d54e56fe --- /dev/null +++ b/app/include/ssl/ssl_os_port.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file os_port.h + * + * Some stuff to minimise the differences between windows and linux/unix + */ + +#ifndef HEADER_OS_PORT_H +#define HEADER_OS_PORT_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#include "../crypto/os_int.h" +#include "c_types.h" +#include "osapi.h" +#include + +#if 0 +#define ssl_printf(fmt, args...) os_printf(fmt,## args) +#else +#define ssl_printf(fmt, args...) +#endif + +#define STDCALL +#define EXP_FUNC + +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; + +/* Mutexing definitions */ + +#define SSL_CTX_MUTEX_INIT(A) +#define SSL_CTX_MUTEX_DESTROY(A) +#define SSL_CTX_LOCK(A) +#define SSL_CTX_UNLOCK(A) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/include/ssl/ssl_private_key.h b/app/include/ssl/ssl_private_key.h new file mode 100644 index 00000000..ce7985c5 --- /dev/null +++ b/app/include/ssl/ssl_private_key.h @@ -0,0 +1,54 @@ +unsigned char default_private_key[] = { + 0x30, 0x82, 0x02, 0x5d, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xcd, + 0xfd, 0x89, 0x48, 0xbe, 0x36, 0xb9, 0x95, 0x76, 0xd4, 0x13, 0x30, 0x0e, + 0xbf, 0xb2, 0xed, 0x67, 0x0a, 0xc0, 0x16, 0x3f, 0x51, 0x09, 0x9d, 0x29, + 0x2f, 0xb2, 0x6d, 0x3f, 0x3e, 0x6c, 0x2f, 0x90, 0x80, 0xa1, 0x71, 0xdf, + 0xbe, 0x38, 0xc5, 0xcb, 0xa9, 0x9a, 0x40, 0x14, 0x90, 0x0a, 0xf9, 0xb7, + 0x07, 0x0b, 0xe1, 0xda, 0xe7, 0x09, 0xbf, 0x0d, 0x57, 0x41, 0x86, 0x60, + 0xa1, 0xc1, 0x27, 0x91, 0x5b, 0x0a, 0x98, 0x46, 0x1b, 0xf6, 0xa2, 0x84, + 0xf8, 0x65, 0xc7, 0xce, 0x2d, 0x96, 0x17, 0xaa, 0x91, 0xf8, 0x61, 0x04, + 0x50, 0x70, 0xeb, 0xb4, 0x43, 0xb7, 0xdc, 0x9a, 0xcc, 0x31, 0x01, 0x14, + 0xd4, 0xcd, 0xcc, 0xc2, 0x37, 0x6d, 0x69, 0x82, 0xd6, 0xc6, 0xc4, 0xbe, + 0xf2, 0x34, 0xa5, 0xc9, 0xa6, 0x19, 0x53, 0x32, 0x7a, 0x86, 0x0e, 0x91, + 0x82, 0x0f, 0xa1, 0x42, 0x54, 0xaa, 0x01, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x81, 0x81, 0x00, 0x95, 0xaa, 0x6e, 0x11, 0xf5, 0x6a, 0x8b, 0xa2, + 0xc6, 0x48, 0xc6, 0x7c, 0x37, 0x6b, 0x1f, 0x55, 0x10, 0x76, 0x26, 0x24, + 0xc3, 0xf2, 0x5c, 0x5a, 0xdd, 0x2e, 0xf3, 0xa4, 0x1e, 0xbc, 0x7b, 0x1c, + 0x80, 0x10, 0x85, 0xbc, 0xd8, 0x45, 0x3c, 0xb8, 0xb2, 0x06, 0x53, 0xb5, + 0xd5, 0x7a, 0xe7, 0x0e, 0x92, 0xe6, 0x42, 0xc2, 0xe2, 0x2a, 0xd5, 0xd1, + 0x03, 0x9f, 0x6f, 0x53, 0x74, 0x68, 0x72, 0x8e, 0xbf, 0x03, 0xbb, 0xab, + 0xbd, 0xa1, 0xf9, 0x81, 0x7d, 0x12, 0xd4, 0x9d, 0xb6, 0xae, 0x4c, 0xad, + 0xca, 0xa8, 0xc9, 0x80, 0x8d, 0x0d, 0xd5, 0xd0, 0xa1, 0xbf, 0xec, 0x60, + 0x48, 0x49, 0xed, 0x97, 0x0f, 0x5e, 0xed, 0xfc, 0x39, 0x15, 0x96, 0x9e, + 0x5d, 0xe2, 0xb4, 0x5d, 0x2e, 0x04, 0xdc, 0x08, 0xa2, 0x65, 0x29, 0x2d, + 0x37, 0xfb, 0x62, 0x90, 0x1b, 0x7b, 0xe5, 0x3a, 0x58, 0x05, 0x55, 0xc1, + 0x02, 0x41, 0x00, 0xfc, 0x69, 0x28, 0xc9, 0xa8, 0xc4, 0x5c, 0xe3, 0xd0, + 0x5e, 0xaa, 0xda, 0xde, 0x87, 0x74, 0xdb, 0xcb, 0x40, 0x78, 0x8e, 0x1d, + 0x12, 0x96, 0x16, 0x61, 0x3f, 0xb3, 0x3e, 0xa3, 0x0d, 0xdc, 0x49, 0xa5, + 0x25, 0x87, 0xc5, 0x97, 0x85, 0x9d, 0xbb, 0xb4, 0xf0, 0x44, 0xfd, 0x6c, + 0xe8, 0xd2, 0x8c, 0xec, 0x33, 0x81, 0x46, 0x1e, 0x10, 0x12, 0x33, 0x16, + 0x95, 0x00, 0x4f, 0x75, 0xb4, 0xe5, 0x79, 0x02, 0x41, 0x00, 0xd0, 0xeb, + 0x65, 0x07, 0x10, 0x3b, 0xd9, 0x03, 0xeb, 0xdc, 0x6f, 0x4b, 0x8f, 0xc3, + 0x87, 0xce, 0x76, 0xd6, 0xc5, 0x14, 0x21, 0x4e, 0xe7, 0x4f, 0x1b, 0xe8, + 0x05, 0xf8, 0x84, 0x1a, 0xe0, 0xc5, 0xd6, 0xe3, 0x08, 0xb3, 0x54, 0x57, + 0x02, 0x1f, 0xd4, 0xd9, 0xfb, 0xff, 0x40, 0xb1, 0x56, 0x1c, 0x60, 0xf7, + 0xac, 0x91, 0xf3, 0xd3, 0xc6, 0x7f, 0x84, 0xfd, 0x84, 0x9d, 0xea, 0x26, + 0xee, 0xc9, 0x02, 0x41, 0x00, 0xa6, 0xcf, 0x1c, 0x6c, 0x81, 0x03, 0x1c, + 0x5c, 0x56, 0x05, 0x6a, 0x26, 0x70, 0xef, 0xd6, 0x13, 0xb7, 0x74, 0x28, + 0xf7, 0xca, 0x50, 0xd1, 0x2d, 0x83, 0x21, 0x64, 0xe4, 0xdd, 0x3f, 0x38, + 0xb8, 0xd6, 0xd2, 0x41, 0xb3, 0x1c, 0x9a, 0xea, 0x0d, 0xf5, 0xda, 0xdf, + 0xcd, 0x17, 0x9f, 0x9a, 0x1e, 0x15, 0xaf, 0x48, 0x1c, 0xbd, 0x9b, 0x63, + 0x5b, 0xad, 0xed, 0xd4, 0xa1, 0xae, 0xa9, 0x59, 0x09, 0x02, 0x40, 0x4e, + 0x08, 0xce, 0xa8, 0x8f, 0xc0, 0xba, 0xf3, 0x83, 0x02, 0xc8, 0x33, 0x62, + 0x14, 0x77, 0xc2, 0x7f, 0x93, 0x02, 0xf3, 0xdc, 0xe9, 0x1a, 0xee, 0xea, + 0x8e, 0x84, 0xc4, 0x69, 0x9b, 0x9c, 0x7f, 0x69, 0x1f, 0x4e, 0x1d, 0xa5, + 0x90, 0x06, 0x44, 0x1b, 0x7d, 0xfc, 0x69, 0x40, 0x21, 0xbc, 0xf7, 0x46, + 0xa4, 0xdc, 0x39, 0x7b, 0xe8, 0x8b, 0x49, 0x10, 0x44, 0x9d, 0x67, 0x5a, + 0x91, 0x86, 0x39, 0x02, 0x40, 0x41, 0x2c, 0x4e, 0xfe, 0xd9, 0x90, 0x89, + 0x00, 0x5c, 0x94, 0x0a, 0x4a, 0x7e, 0x1b, 0x1a, 0x80, 0x06, 0x01, 0x37, + 0xda, 0x50, 0x61, 0x9d, 0x9c, 0xfe, 0x25, 0x7f, 0xd8, 0xd4, 0xc4, 0x9e, + 0x81, 0xf2, 0x0c, 0x1e, 0x38, 0x21, 0x1e, 0x90, 0x3f, 0xd4, 0xba, 0x6c, + 0x53, 0xcb, 0xf0, 0x77, 0x79, 0x9b, 0xf1, 0xfa, 0x3f, 0x81, 0xdc, 0xf3, + 0x21, 0x02, 0x6d, 0xb7, 0x95, 0xc3, 0x2e, 0xce, 0xd5 +}; +unsigned int default_private_key_len = 609; diff --git a/app/include/ssl/ssl_ssl.h b/app/include/ssl/ssl_ssl.h new file mode 100644 index 00000000..1107ca70 --- /dev/null +++ b/app/include/ssl/ssl_ssl.h @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @mainpage axTLS API + * + * @image html axolotl.jpg + * + * The axTLS library has features such as: + * - The TLSv1 SSL client/server protocol + * - No requirement to use any openssl libraries. + * - A choice between AES block (128/256 bit) and RC4 (128 bit) stream ciphers. + * - RSA encryption/decryption with variable sized keys (up to 4096 bits). + * - Certificate chaining and peer authentication. + * - Session resumption, session renegotiation. + * - ASN.1, X.509, PKCS#8, PKCS#12 keys/certificates with DER/PEM encoding. + * - Highly configurable compile time options. + * - Portable across many platforms (written in ANSI C), and has language + * bindings in C, C#, VB.NET, Java, Perl and Lua. + * - Partial openssl API compatibility (via a wrapper). + * - A very small footprint (around 50-60kB for the library in 'server-only' + * mode). + * - No dependencies on sockets - can use serial connections for example. + * - A very simple API - ~ 20 functions/methods. + * + * A list of these functions/methods are described below. + * + * @ref c_api + * + * @ref bigint_api + * + * @ref csharp_api + * + * @ref java_api + */ +#ifndef HEADER_SSL_H +#define HEADER_SSL_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#include +typedef long time_t; + +/* need to predefine before ssl_lib.h gets to it */ +#define SSL_SESSION_ID_SIZE 32 + +#include "ssl/ssl_tls1.h" + +/* The optional parameters that can be given to the client/server SSL engine */ +#define SSL_CLIENT_AUTHENTICATION 0x00010000 +#define SSL_SERVER_VERIFY_LATER 0x00020000 +#define SSL_NO_DEFAULT_KEY 0x00040000 +#define SSL_DISPLAY_STATES 0x00080000 +#define SSL_DISPLAY_BYTES 0x00100000 +#define SSL_DISPLAY_CERTS 0x00200000 +#define SSL_DISPLAY_RSA 0x00400000 +#define SSL_CONNECT_IN_PARTS 0x00800000 + +/* errors that can be generated */ +#define SSL_OK 0 +#define SSL_NOT_OK -1 +#define SSL_ERROR_DEAD -2 +#define SSL_CLOSE_NOTIFY -3 +#define SSL_ERROR_CONN_LOST -256 +#define SSL_ERROR_SOCK_SETUP_FAILURE -258 +#define SSL_ERROR_INVALID_HANDSHAKE -260 +#define SSL_ERROR_INVALID_PROT_MSG -261 +#define SSL_ERROR_INVALID_HMAC -262 +#define SSL_ERROR_INVALID_VERSION -263 +#define SSL_ERROR_INVALID_SESSION -265 +#define SSL_ERROR_NO_CIPHER -266 +#define SSL_ERROR_BAD_CERTIFICATE -268 +#define SSL_ERROR_INVALID_KEY -269 +#define SSL_ERROR_FINISHED_INVALID -271 +#define SSL_ERROR_NO_CERT_DEFINED -272 +#define SSL_ERROR_NO_CLIENT_RENOG -273 +#define SSL_ERROR_NOT_SUPPORTED -274 +#define SSL_X509_OFFSET -512 +#define SSL_X509_ERROR(A) (SSL_X509_OFFSET+A) + +/* alert types that are recognized */ +#define SSL_ALERT_TYPE_WARNING 1 +#define SLL_ALERT_TYPE_FATAL 2 + +/* these are all the alerts that are recognized */ +#define SSL_ALERT_CLOSE_NOTIFY 0 +#define SSL_ALERT_UNEXPECTED_MESSAGE 10 +#define SSL_ALERT_BAD_RECORD_MAC 20 +#define SSL_ALERT_HANDSHAKE_FAILURE 40 +#define SSL_ALERT_BAD_CERTIFICATE 42 +#define SSL_ALERT_ILLEGAL_PARAMETER 47 +#define SSL_ALERT_DECODE_ERROR 50 +#define SSL_ALERT_DECRYPT_ERROR 51 +#define SSL_ALERT_INVALID_VERSION 70 +#define SSL_ALERT_NO_RENEGOTIATION 100 + +/* The ciphers that are supported */ +#define SSL_AES128_SHA 0x2f +#define SSL_AES256_SHA 0x35 +#define SSL_RC4_128_SHA 0x05 +#define SSL_RC4_128_MD5 0x04 + +/* build mode ids' */ +#define SSL_BUILD_SKELETON_MODE 0x01 +#define SSL_BUILD_SERVER_ONLY 0x02 +#define SSL_BUILD_ENABLE_VERIFICATION 0x03 +#define SSL_BUILD_ENABLE_CLIENT 0x04 +#define SSL_BUILD_FULL_MODE 0x05 + +/* offsets to retrieve configuration information */ +#define SSL_BUILD_MODE 0 +#define SSL_MAX_CERT_CFG_OFFSET 1 +#define SSL_MAX_CA_CERT_CFG_OFFSET 2 +#define SSL_HAS_PEM 3 + +/* default session sizes */ +#define SSL_DEFAULT_SVR_SESS 1 //modify 5->1 by lhan +#define SSL_DEFAULT_CLNT_SESS 1 + +/* X.509/X.520 distinguished name types */ +#define SSL_X509_CERT_COMMON_NAME 0 +#define SSL_X509_CERT_ORGANIZATION 1 +#define SSL_X509_CERT_ORGANIZATIONAL_NAME 2 +#define SSL_X509_CA_CERT_COMMON_NAME 3 +#define SSL_X509_CA_CERT_ORGANIZATION 4 +#define SSL_X509_CA_CERT_ORGANIZATIONAL_NAME 5 + +/* SSL object loader types */ +#define SSL_OBJ_X509_CERT 1 +#define SSL_OBJ_X509_CACERT 2 +#define SSL_OBJ_RSA_KEY 3 +#define SSL_OBJ_PKCS8 4 +#define SSL_OBJ_PKCS12 5 + +/** + * @defgroup c_api Standard C API + * @brief The standard interface in C. + * @{ + */ + +/** + * @brief Establish a new client/server context. + * + * This function is called before any client/server SSL connections are made. + * + * Each new connection will use the this context's private key and + * certificate chain. If a different certificate chain is required, then a + * different context needs to be be used. + * + * There are two threading models supported - a single thread with one + * SSL_CTX can support any number of SSL connections - and multiple threads can + * support one SSL_CTX object each (the default). But if a single SSL_CTX + * object uses many SSL objects in individual threads, then the + * CONFIG_SSL_CTX_MUTEXING option needs to be configured. + * + * @param options [in] Any particular options. At present the options + * supported are: + * - SSL_SERVER_VERIFY_LATER (client only): Don't stop a handshake if the server + * authentication fails. The certificate can be authenticated later with a + * call to ssl_verify_cert(). + * - SSL_CLIENT_AUTHENTICATION (server only): Enforce client authentication + * i.e. each handshake will include a "certificate request" message from the + * server. Only available if verification has been enabled. + * - SSL_DISPLAY_BYTES (full mode build only): Display the byte sequences + * during the handshake. + * - SSL_DISPLAY_STATES (full mode build only): Display the state changes + * during the handshake. + * - SSL_DISPLAY_CERTS (full mode build only): Display the certificates that + * are passed during a handshake. + * - SSL_DISPLAY_RSA (full mode build only): Display the RSA key details that + * are passed during a handshake. + * - SSL_CONNECT_IN_PARTS (client only): To use a non-blocking version of + * ssl_client_new(). + * @param num_sessions [in] The number of sessions to be used for session + * caching. If this value is 0, then there is no session caching. This option + * is not used in skeleton mode. + * @return A client/server context. + */ +EXP_FUNC SSL_CTX * STDCALL ssl_ctx_new(uint32_t options, int num_sessions); + +/** + * @brief Remove a client/server context. + * + * Frees any used resources used by this context. Each connection will be + * sent a "Close Notify" alert (if possible). + * @param ssl_ctx [in] The client/server context. + */ +EXP_FUNC void STDCALL ssl_ctx_free(SSL_CTX *ssl_ctx); + +/** + * @brief (server only) Establish a new SSL connection to an SSL client. + * + * It is up to the application to establish the logical connection (whether it + * is a socket, serial connection etc). + * @param ssl_ctx [in] The server context. + * @param client_fd [in] The client's file descriptor. + * @return An SSL object reference. + */ +//EXP_FUNC SSL * STDCALL ssl_server_new(SSL_CTX *ssl_ctx, int client_fd); + +EXP_FUNC SSL *STDCALL sslserver_new(SSL_CTX *ssl_ctx, struct tcp_pcb* client_pcb); +/** + * @brief (client only) Establish a new SSL connection to an SSL server. + * + * It is up to the application to establish the initial logical connection + * (whether it is a socket, serial connection etc). + * + * This is a normally a blocking call - it will finish when the handshake is + * complete (or has failed). To use in non-blocking mode, set + * SSL_CONNECT_IN_PARTS in ssl_ctx_new(). + * @param ssl_ctx [in] The client context. + * @param client_fd [in] The client's file descriptor. + * @param session_id [in] A 32 byte session id for session resumption. This + * can be null if no session resumption is being used or required. This option + * is not used in skeleton mode. + * @param sess_id_size The size of the session id (max 32) + * @return An SSL object reference. Use ssl_handshake_status() to check + * if a handshake succeeded. + */ +//EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const uint8_t *session_id, uint8_t sess_id_size); + +EXP_FUNC SSL *STDCALL SSLClient_new(SSL_CTX *ssl_ctx, struct tcp_pcb *SslClient_pcb, const + uint8_t *session_id, uint8_t sess_id_size); +/** + * @brief Free any used resources on this connection. + + * A "Close Notify" message is sent on this connection (if possible). It is up + * to the application to close the socket or file descriptor. + * @param ssl [in] The ssl object reference. + */ +EXP_FUNC void STDCALL ssl_free(SSL *ssl); + +/** + * @brief Read the SSL data stream. + * If the socket is non-blocking and data is blocked then SSO_OK will be + * returned. + * @param ssl [in] An SSL object reference. + * @param in_data [out] If the read was successful, a pointer to the read + * buffer will be here. Do NOT ever free this memory as this buffer is used in + * sucessive calls. If the call was unsuccessful, this value will be null. + * @return The number of decrypted bytes: + * - if > 0, then the handshaking is complete and we are returning the number + * of decrypted bytes. + * - SSL_OK if the handshaking stage is successful (but not yet complete). + * - < 0 if an error. + * @see ssl.h for the error code list. + * @note Use in_data before doing any successive ssl calls. + */ +EXP_FUNC int STDCALL ssl_read(SSL *ssl, uint8_t **in_data); + +/** + * @brief Write to the SSL data stream. + * if the socket is non-blocking and data is blocked then a check is made + * to ensure that all data is sent (i.e. blocked mode is forced). + * @param ssl [in] An SSL obect reference. + * @param out_data [in] The data to be written + * @param out_len [in] The number of bytes to be written. + * @return The number of bytes sent, or if < 0 if an error. + * @see ssl.h for the error code list. + */ +EXP_FUNC int STDCALL ssl_write(SSL *ssl, const uint8_t *out_data, int out_len); + +/** + * @brief Find an ssl object based on a file descriptor. + * + * Goes through the list of SSL objects maintained in a client/server context + * to look for a file descriptor match. + * @param ssl_ctx [in] The client/server context. + * @param client_fd [in] The file descriptor. + * @return A reference to the SSL object. Returns null if the object could not + * be found. + */ +EXP_FUNC SSL * STDCALL ssl_find(SSL_CTX *ssl_ctx, int client_fd); + +/** + * @brief Get the session id for a handshake. + * + * This will be a 32 byte sequence and is available after the first + * handshaking messages are sent. + * @param ssl [in] An SSL object reference. + * @return The session id as a 32 byte sequence. + * @note A SSLv23 handshake may have only 16 valid bytes. + */ +EXP_FUNC const uint8_t * STDCALL ssl_get_session_id(const SSL *ssl); + +/** + * @brief Get the session id size for a handshake. + * + * This will normally be 32 but could be 0 (no session id) or something else. + * @param ssl [in] An SSL object reference. + * @return The size of the session id. + */ +EXP_FUNC uint8_t STDCALL ssl_get_session_id_size(const SSL *ssl); + +/** + * @brief Return the cipher id (in the SSL form). + * @param ssl [in] An SSL object reference. + * @return The cipher id. This will be one of the following: + * - SSL_AES128_SHA (0x2f) + * - SSL_AES256_SHA (0x35) + * - SSL_RC4_128_SHA (0x05) + * - SSL_RC4_128_MD5 (0x04) + */ +EXP_FUNC uint8_t STDCALL ssl_get_cipher_id(const SSL *ssl); + +/** + * @brief Return the status of the handshake. + * @param ssl [in] An SSL object reference. + * @return SSL_OK if the handshake is complete and ok. + * @see ssl.h for the error code list. + */ +EXP_FUNC int STDCALL ssl_handshake_status(const SSL *ssl); + +/** + * @brief Retrieve various parameters about the axTLS engine. + * @param offset [in] The configuration offset. It will be one of the following: + * - SSL_BUILD_MODE The build mode. This will be one of the following: + * - SSL_BUILD_SERVER_ONLY (basic server mode) + * - SSL_BUILD_ENABLE_VERIFICATION (server can do client authentication) + * - SSL_BUILD_ENABLE_CLIENT (client/server capabilties) + * - SSL_BUILD_FULL_MODE (client/server with diagnostics) + * - SSL_BUILD_SKELETON_MODE (skeleton mode) + * - SSL_MAX_CERT_CFG_OFFSET The maximum number of certificates allowed. + * - SSL_MAX_CA_CERT_CFG_OFFSET The maximum number of CA certificates allowed. + * - SSL_HAS_PEM 1 if supported + * @return The value of the requested parameter. + */ +EXP_FUNC int STDCALL ssl_get_config(int offset); + +/** + * @brief Display why the handshake failed. + * + * This call is only useful in a 'full mode' build. The output is to stdout. + * @param error_code [in] An error code. + * @see ssl.h for the error code list. + */ +//EXP_FUNC void STDCALL ssl_display_error(int error_code); + +/** + * @brief Authenticate a received certificate. + * + * This call is usually made by a client after a handshake is complete and the + * context is in SSL_SERVER_VERIFY_LATER mode. + * @param ssl [in] An SSL object reference. + * @return SSL_OK if the certificate is verified. + */ +EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl); + +/** + * @brief Retrieve an X.509 distinguished name component. + * + * When a handshake is complete and a certificate has been exchanged, then the + * details of the remote certificate can be retrieved. + * + * This will usually be used by a client to check that the server's common + * name matches the URL. + * + * @param ssl [in] An SSL object reference. + * @param component [in] one of: + * - SSL_X509_CERT_COMMON_NAME + * - SSL_X509_CERT_ORGANIZATION + * - SSL_X509_CERT_ORGANIZATIONAL_NAME + * - SSL_X509_CA_CERT_COMMON_NAME + * - SSL_X509_CA_CERT_ORGANIZATION + * - SSL_X509_CA_CERT_ORGANIZATIONAL_NAME + * @return The appropriate string (or null if not defined) + * @note Verification build mode must be enabled. + */ +EXP_FUNC const char * STDCALL ssl_get_cert_dn(const SSL *ssl, int component); + +/** + * @brief Retrieve a Subject Alternative DNSName + * + * When a handshake is complete and a certificate has been exchanged, then the + * details of the remote certificate can be retrieved. + * + * This will usually be used by a client to check that the server's DNS + * name matches the URL. + * + * @param ssl [in] An SSL object reference. + * @param dnsindex [in] The index of the DNS name to retrieve. + * @return The appropriate string (or null if not defined) + * @note Verification build mode must be enabled. + */ +EXP_FUNC const char * STDCALL ssl_get_cert_subject_alt_dnsname(const SSL *ssl, int dnsindex); + +/** + * @brief Force the client to perform its handshake again. + * + * For a client this involves sending another "client hello" message. + * For the server is means sending a "hello request" message. + * + * This is a blocking call on the client (until the handshake completes). + * + * @param ssl [in] An SSL object reference. + * @return SSL_OK if renegotiation instantiation was ok + */ +EXP_FUNC int STDCALL ssl_renegotiate(SSL *ssl); + +/** + * @brief Process a file that is in binary DER or ASCII PEM format. + * + * These are temporary objects that are used to load private keys, + * certificates etc into memory. + * @param ssl_ctx [in] The client/server context. + * @param obj_type [in] The format of the file. Can be one of: + * - SSL_OBJ_X509_CERT (no password required) + * - SSL_OBJ_X509_CACERT (no password required) + * - SSL_OBJ_RSA_KEY (AES128/AES256 PEM encryption supported) + * - SSL_OBJ_PKCS8 (RC4-128 encrypted data supported) + * - SSL_OBJ_PKCS12 (RC4-128 encrypted data supported) + * + * PEM files are automatically detected (if supported). The object type is + * also detected, and so is not relevant for these types of files. + * @param filename [in] The location of a file in DER/PEM format. + * @param password [in] The password used. Can be null if not required. + * @return SSL_OK if all ok + * @note Not available in skeleton build mode. + */ +EXP_FUNC int STDCALL ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type, const char *filename, const char *password); + +/** + * @brief Process binary data. + * + * These are temporary objects that are used to load private keys, + * certificates etc into memory. + * @param ssl_ctx [in] The client/server context. + * @param obj_type [in] The format of the memory data. + * @param data [in] The binary data to be loaded. + * @param len [in] The amount of data to be loaded. + * @param password [in] The password used. Can be null if not required. + * @return SSL_OK if all ok + * @see ssl_obj_load for more details on obj_type. + */ +EXP_FUNC int STDCALL ssl_obj_memory_load(SSL_CTX *ssl_ctx, int obj_type, const uint8_t *data, int len, const char *password); + +#ifdef CONFIG_SSL_GENERATE_X509_CERT +/** + * @brief Create an X.509 certificate. + * + * This certificate is a self-signed v1 cert with a fixed start/stop validity + * times. It is signed with an internal private key in ssl_ctx. + * + * @param ssl_ctx [in] The client/server context. + * @param options [in] Not used yet. + * @param dn [in] An array of distinguished name strings. The array is defined + * by: + * - SSL_X509_CERT_COMMON_NAME (0) + * - If SSL_X509_CERT_COMMON_NAME is empty or not defined, then the + * hostname will be used. + * - SSL_X509_CERT_ORGANIZATION (1) + * - If SSL_X509_CERT_ORGANIZATION is empty or not defined, then $USERNAME + * will be used. + * - SSL_X509_CERT_ORGANIZATIONAL_NAME (2) + * - SSL_X509_CERT_ORGANIZATIONAL_NAME is optional. + * @param cert_data [out] The certificate as a sequence of bytes. + * @return < 0 if an error, or the size of the certificate in bytes. + * @note cert_data must be freed when there is no more need for it. + */ +EXP_FUNC int STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, uint32_t options, const char * dn[], uint8_t **cert_data); +#endif + +/** + * @brief Return the axTLS library version as a string. + */ +EXP_FUNC const char * STDCALL ssl_version(void); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/include/ssl/ssl_tls1.h b/app/include/ssl/ssl_tls1.h new file mode 100644 index 00000000..5b72f202 --- /dev/null +++ b/app/include/ssl/ssl_tls1.h @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file tls1.h + * + * @brief The definitions for the TLS library. + */ +#ifndef HEADER_SSL_LIB_H +#define HEADER_SSL_LIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "c_types.h" +#include "ssl/ssl_version.h" +#include "ssl/ssl_config.h" +//#include "../crypto/os_int.h" +#include "ssl/ssl_crypto.h" +#include "ssl/ssl_crypto_misc.h" +#include "lwip/tcp.h" + +#define SSL_PROTOCOL_MIN_VERSION 0x31 /* TLS v1.0 */ +#define SSL_PROTOCOL_MINOR_VERSION 0x02 /* TLS v1.1 */ +#define SSL_PROTOCOL_VERSION_MAX 0x32 /* TLS v1.1 */ +#define SSL_PROTOCOL_VERSION1_1 0x32 /* TLS v1.1 */ +#define SSL_RANDOM_SIZE 32 +#define SSL_SECRET_SIZE 48 +#define SSL_FINISHED_HASH_SIZE 12 +#define SSL_RECORD_SIZE 5 +#define SSL_SERVER_READ 0 +#define SSL_SERVER_WRITE 1 +#define SSL_CLIENT_READ 2 +#define SSL_CLIENT_WRITE 3 +#define SSL_HS_HDR_SIZE 4 + +/* the flags we use while establishing a connection */ +#define SSL_NEED_RECORD 0x0001 +#define SSL_TX_ENCRYPTED 0x0002 +#define SSL_RX_ENCRYPTED 0x0004 +#define SSL_SESSION_RESUME 0x0008 +#define SSL_IS_CLIENT 0x0010 +#define SSL_HAS_CERT_REQ 0x0020 +#define SSL_SENT_CLOSE_NOTIFY 0x0040 + +/* some macros to muck around with flag bits */ +#define SET_SSL_FLAG(A) (ssl->flag |= A) +#define CLR_SSL_FLAG(A) (ssl->flag &= ~A) +#define IS_SET_SSL_FLAG(A) (ssl->flag & A) + +#define MAX_KEY_BYTE_SIZE 512 /* for a 4096 bit key */ +#define RT_MAX_PLAIN_LENGTH 4096 +#define RT_EXTRA 1024 +#define BM_RECORD_OFFSET 5 + +#ifdef CONFIG_SSL_SKELETON_MODE +#define NUM_PROTOCOLS 1 +#else +#define NUM_PROTOCOLS 4 +#endif + +#define PARANOIA_CHECK(A, B) if (A < B) { \ + ret = SSL_ERROR_INVALID_HANDSHAKE; goto error; } + +/* protocol types */ +enum +{ + PT_CHANGE_CIPHER_SPEC = 20, + PT_ALERT_PROTOCOL, + PT_HANDSHAKE_PROTOCOL, + PT_APP_PROTOCOL_DATA +}; + +/* handshaking types */ +enum +{ + HS_HELLO_REQUEST, + HS_CLIENT_HELLO, + HS_SERVER_HELLO, + HS_CERTIFICATE = 11, + HS_SERVER_KEY_XCHG, + HS_CERT_REQ, + HS_SERVER_HELLO_DONE, + HS_CERT_VERIFY, + HS_CLIENT_KEY_XCHG, + HS_FINISHED = 20 +}; + +typedef struct +{ + uint8_t cipher; + uint8_t key_size; + uint8_t iv_size; + uint8_t key_block_size; + uint8_t padding_size; + uint8_t digest_size; + hmac_func hmac; + crypt_func encrypt; + crypt_func decrypt; +} cipher_info_t; + +struct _SSLObjLoader +{ + uint8_t *buf; + int len; +}; + +typedef struct _SSLObjLoader SSLObjLoader; + +typedef struct +{ + time_t conn_time; + uint8_t session_id[SSL_SESSION_ID_SIZE]; + uint8_t master_secret[SSL_SECRET_SIZE]; +} SSL_SESSION; + +typedef struct +{ + uint8_t *buf; + int size; +} SSL_CERT; + +typedef struct +{ + MD5_CTX md5_ctx; + SHA1_CTX sha1_ctx; + uint8_t final_finish_mac[SSL_FINISHED_HASH_SIZE]; + uint8_t *key_block; + uint8_t master_secret[SSL_SECRET_SIZE]; + uint8_t client_random[SSL_RANDOM_SIZE]; /* client's random sequence */ + uint8_t server_random[SSL_RANDOM_SIZE]; /* server's random sequence */ + uint16_t bm_proc_index; +} DISPOSABLE_CTX; + +struct _SSL +{ + uint32_t flag; + uint16_t need_bytes; + uint16_t got_bytes; + uint8_t record_type; + uint8_t cipher; + uint8_t sess_id_size; + uint8_t version; + uint8_t client_version; + sint16_t next_state; + sint16_t hs_status; + DISPOSABLE_CTX *dc; /* temporary data which we'll get rid of soon */ + //int client_fd; + struct tcp_pcb *SslClient_pcb;//add by ives 12.12.2013 + struct pbuf *ssl_pbuf;//add by ives 12.12.2013 + const cipher_info_t *cipher_info; + void *encrypt_ctx; + void *decrypt_ctx; + uint8_t bm_all_data[RT_MAX_PLAIN_LENGTH+RT_EXTRA]; + uint8_t *bm_data; + uint16_t bm_index; + uint16_t bm_read_index; + struct _SSL *next; /* doubly linked list */ + struct _SSL *prev; + struct _SSL_CTX *ssl_ctx; /* back reference to a clnt/svr ctx */ +#ifndef CONFIG_SSL_SKELETON_MODE + uint16_t session_index; + SSL_SESSION *session; +#endif +#ifdef CONFIG_SSL_CERT_VERIFICATION + X509_CTX *x509_ctx; +#endif + + uint8_t session_id[SSL_SESSION_ID_SIZE]; + uint8_t client_mac[SHA1_SIZE]; /* for HMAC verification */ + uint8_t server_mac[SHA1_SIZE]; /* for HMAC verification */ + uint8_t read_sequence[8]; /* 64 bit sequence number */ + uint8_t write_sequence[8]; /* 64 bit sequence number */ + uint8_t hmac_header[SSL_RECORD_SIZE]; /* rx hmac */ +}; + +typedef struct _SSL SSL; + +struct _SSL_CTX +{ + uint32_t options; + uint8_t chain_length; + RSA_CTX *rsa_ctx; +#ifdef CONFIG_SSL_CERT_VERIFICATION + CA_CERT_CTX *ca_cert_ctx; +#endif + SSL *head; + SSL *tail; + SSL_CERT certs[CONFIG_SSL_MAX_CERTS]; +#ifndef CONFIG_SSL_SKELETON_MODE + uint16_t num_sessions; + SSL_SESSION **ssl_sessions; +#endif +#ifdef CONFIG_SSL_CTX_MUTEXING + SSL_CTX_MUTEX_TYPE mutex; +#endif +#ifdef CONFIG_OPENSSL_COMPATIBLE + void *bonus_attr; +#endif +}; + +typedef struct _SSL_CTX SSL_CTX; + +/* backwards compatibility */ +typedef struct _SSL_CTX SSLCTX; + +extern const uint8_t ssl_prot_prefs[NUM_PROTOCOLS]; + +SSL *ssl_new(SSL_CTX *ssl_ctx, int client_fd); +SSL *ssl_new_context(SSL_CTX *ssl_ctx, struct tcp_pcb *SslClient_pcb); +void disposable_new(SSL *ssl); +void disposable_free(SSL *ssl); +int send_packet(SSL *ssl, uint8_t protocol, + const uint8_t *in, int length); +int do_svr_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len); +int do_clnt_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len); +int process_finished(SSL *ssl, uint8_t *buf, int hs_len); +int process_sslv23_client_hello(SSL *ssl); +int send_alert(SSL *ssl, int error_code); +int send_finished(SSL *ssl); +int send_certificate(SSL *ssl); +int basic_read(SSL *ssl, uint8_t **in_data); +int send_change_cipher_spec(SSL *ssl); +void finished_digest(SSL *ssl, const char *label, uint8_t *digest); +void generate_master_secret(SSL *ssl, const uint8_t *premaster_secret); +void add_packet(SSL *ssl, const uint8_t *pkt, int len); +int add_cert(SSL_CTX *ssl_ctx, const uint8_t *buf, int len); +int add_private_key(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj); +void ssl_obj_free(SSLObjLoader *ssl_obj); +int pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password); +int pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password); +int load_key_certs(SSL_CTX *ssl_ctx); +#ifdef CONFIG_SSL_CERT_VERIFICATION +int add_cert_auth(SSL_CTX *ssl_ctx, const uint8_t *buf, int len); +void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx); +#endif +#ifdef CONFIG_SSL_ENABLE_CLIENT +int do_client_connect(SSL *ssl); +#endif + +#ifdef CONFIG_SSL_FULL_MODE +//void DISPLAY_STATE(SSL *ssl, int is_send, uint8_t state, int not_ok); +//void DISPLAY_BYTES(SSL *ssl, const char *format, +// const uint8_t *data, int size, ...); +//void DISPLAY_CERT(SSL *ssl, const X509_CTX *x509_ctx); +//void DISPLAY_RSA(SSL *ssl, const RSA_CTX *rsa_ctx); +//void DISPLAY_ALERT(SSL *ssl, int alert); +#else +#define DISPLAY_STATE(A,B,C,D) +#define DISPLAY_CERT(A,B) +#define DISPLAY_RSA(A,B) +#define DISPLAY_ALERT(A, B) +#ifdef WIN32 +void DISPLAY_BYTES(SSL *ssl, const char *format,/* win32 has no variadic macros */ + const uint8_t *data, int size, ...); +#else +#define DISPLAY_BYTES(A,B,C,D,...) +#endif +#endif + +#ifdef CONFIG_SSL_CERT_VERIFICATION +int process_certificate(SSL *ssl, X509_CTX **x509_ctx); +#endif + +SSL_SESSION *ssl_session_update(int max_sessions, + SSL_SESSION *ssl_sessions[], SSL *ssl, + const uint8_t *session_id); +void kill_ssl_session(SSL_SESSION **ssl_sessions, SSL *ssl); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/include/ssl/ssl_version.h b/app/include/ssl/ssl_version.h new file mode 100644 index 00000000..e8158cc0 --- /dev/null +++ b/app/include/ssl/ssl_version.h @@ -0,0 +1 @@ +#define AXTLS_VERSION "1.4.9" diff --git a/app/include/user_config.h b/app/include/user_config.h new file mode 100644 index 00000000..21074009 --- /dev/null +++ b/app/include/user_config.h @@ -0,0 +1,71 @@ +#ifndef __USER_CONFIG_H__ +#define __USER_CONFIG_H__ + +#define NODE_VERSION "NodeMcu 0.9.4" +#define BUILD_DATE "build 20141222" +#define FLASH_512K +// #define FLASH_1M +// #define FLASH_2M +// #define FLASH_4M +// #define DEVELOP_VERSION +#define FULL_VERSION_FOR_USER + +#ifdef DEVELOP_VERSION +#define NODE_DEBUG +#endif /* DEVELOP_VERSION */ + +#define NODE_ERROR + +#ifdef NODE_DEBUG +#define NODE_DBG c_printf +#else +#define NODE_DBG +#endif /* NODE_DEBUG */ + +#ifdef NODE_ERROR +#define NODE_ERR c_printf +#else +#define NODE_ERR +#endif /* NODE_ERROR */ + +#define CLIENT_SSL_ENABLE +#define GPIO_INTERRUPT_ENABLE + +// #define BUILD_WOFS 1 +#define BUILD_SPIFFS 1 + +#define LUA_USE_MODULES +#ifdef LUA_USE_MODULES +#define LUA_USE_MODULES_NODE +#define LUA_USE_MODULES_FILE +#define LUA_USE_MODULES_GPIO +#define LUA_USE_MODULES_WIFI +#define LUA_USE_MODULES_NET +#define LUA_USE_MODULES_PWM +#define LUA_USE_MODULES_I2C +#define LUA_USE_MODULES_TMR +#define LUA_USE_MODULES_ADC +#define LUA_USE_MODULES_UART +#define LUA_USE_MODULES_OW +#define LUA_USE_MODULES_BIT +#endif /* LUA_USE_MODULES */ + +#define LUA_NUMBER_INTEGRAL + +#define LUA_OPTRAM +#ifdef LUA_OPTRAM +#define LUA_OPTIMIZE_MEMORY 2 +#else +#define LUA_OPTIMIZE_MEMORY 0 +#endif /* LUA_OPTRAM */ + +#define READLINE_INTERVAL 80 +#define KEY_SHORT_MS 200 +#define KEY_LONG_MS 3000 +#define KEY_SHORT_COUNT (KEY_SHORT_MS / READLINE_INTERVAL) +#define KEY_LONG_COUNT (KEY_LONG_MS / READLINE_INTERVAL) + +#define LED_HIGH_COUNT_DEFAULT 10 +#define LED_LOW_COUNT_DEFAULT 0 + +#endif /* __USER_CONFIG_H__ */ diff --git a/app/json/Makefile b/app/json/Makefile new file mode 100644 index 00000000..37a0d892 --- /dev/null +++ b/app/json/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = libjson.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/json/jsonparse.c b/app/json/jsonparse.c new file mode 100644 index 00000000..f0c9d3a9 --- /dev/null +++ b/app/json/jsonparse.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +#ifdef JSON_FORMAT + +#include "json/jsonparse.h" +#include "osapi.h" +//#include +//#include + +/*--------------------------------------------------------------------*/ +static int ICACHE_FLASH_ATTR +push(struct jsonparse_state *state, char c) +{ + state->stack[state->depth] = c; + state->depth++; + state->vtype = 0; + return state->depth < JSONPARSE_MAX_DEPTH; +} +/*--------------------------------------------------------------------*/ +static char ICACHE_FLASH_ATTR +pop(struct jsonparse_state *state) +{ + if(state->depth == 0) { + return JSON_TYPE_ERROR; + } + state->depth--; + return state->stack[state->depth]; +} +/*--------------------------------------------------------------------*/ +/* will pass by the value and store the start and length of the value for + atomic types */ +/*--------------------------------------------------------------------*/ +static void ICACHE_FLASH_ATTR +atomic(struct jsonparse_state *state, char type) +{ + char c; + + state->vstart = state->pos; + state->vtype = type; + if(type == JSON_TYPE_STRING || type == JSON_TYPE_PAIR_NAME) { + while((c = state->json[state->pos++]) && c != '"') { + if(c == '\\') { + state->pos++; /* skip current char */ + } + } + state->vlen = state->pos - state->vstart - 1; + } else if(type == JSON_TYPE_NUMBER) { + do { + c = state->json[state->pos]; + if((c < '0' || c > '9') && c != '.') { + c = 0; + } else { + state->pos++; + } + } while(c); + /* need to back one step since first char is already gone */ + state->vstart--; + state->vlen = state->pos - state->vstart; + } + /* no other types for now... */ +} +/*--------------------------------------------------------------------*/ +static void ICACHE_FLASH_ATTR +skip_ws(struct jsonparse_state *state) +{ + char c; + + while(state->pos < state->len && + ((c = state->json[state->pos]) == ' ' || c == '\n')) { + state->pos++; + } +} +/*--------------------------------------------------------------------*/ +void ICACHE_FLASH_ATTR +jsonparse_setup(struct jsonparse_state *state, const char *json, int len) +{ + state->json = json; + state->len = len; + state->pos = 0; + state->depth = 0; + state->error = 0; + state->stack[0] = 0; +} +/*--------------------------------------------------------------------*/ +int ICACHE_FLASH_ATTR +jsonparse_next(struct jsonparse_state *state) +{ + char c; + char s; + + skip_ws(state); + c = state->json[state->pos]; + s = jsonparse_get_type(state); + state->pos++; + + switch(c) { + case '{': + push(state, c); + return c; + case '}': + if(s == ':' && state->vtype != 0) { +/* printf("Popping vtype: '%c'\n", state->vtype); */ + pop(state); + s = jsonparse_get_type(state); + } + if(s == '{') { + pop(state); + } else { + state->error = JSON_ERROR_SYNTAX; + return JSON_TYPE_ERROR; + } + return c; + case ']': + if(s == '[') { + pop(state); + } else { + state->error = JSON_ERROR_UNEXPECTED_END_OF_ARRAY; + return JSON_TYPE_ERROR; + } + return c; + case ':': + push(state, c); + return c; + case ',': + /* if x:y ... , */ + if(s == ':' && state->vtype != 0) { + pop(state); + } else if(s == '[') { + /* ok! */ + } else { + state->error = JSON_ERROR_SYNTAX; + return JSON_TYPE_ERROR; + } + return c; + case '"': + if(s == '{' || s == '[' || s == ':') { + atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c)); + } else { + state->error = JSON_ERROR_UNEXPECTED_STRING; + return JSON_TYPE_ERROR; + } + return c; + case '[': + if(s == '{' || s == '[' || s == ':') { + push(state, c); + } else { + state->error = JSON_ERROR_UNEXPECTED_ARRAY; + return JSON_TYPE_ERROR; + } + return c; + default: + if(s == ':' || s == '[') { + if(c <= '9' && c >= '0') { + atomic(state, JSON_TYPE_NUMBER); + return JSON_TYPE_NUMBER; + } + } + } + return 0; +} +/*--------------------------------------------------------------------*/ +/* get the json value of the current position + * works only on "atomic" values such as string, number, null, false, true + */ +/*--------------------------------------------------------------------*/ +int ICACHE_FLASH_ATTR +jsonparse_copy_value(struct jsonparse_state *state, char *str, int size) +{ + int i; + char z = 0; + char y = 0; + + if(state->vtype == 0) { + return 0; + } + size = size <= state->vlen ? (size - 1) : state->vlen; + for(i = 0; i < size; i++) { + if (y == 0 && state->json[state->vstart + i] == '\\') { + y = 1; + z++; + continue; + } + y = 0; + str[i - z] = state->json[state->vstart + i]; + } + str[i - z] = 0; + return state->vtype; +} +/*--------------------------------------------------------------------*/ +int ICACHE_FLASH_ATTR +jsonparse_get_value_as_int(struct jsonparse_state *state) +{ + if(state->vtype != JSON_TYPE_NUMBER) { + return 0; + } + return atoi(&state->json[state->vstart]); +} +/*--------------------------------------------------------------------*/ +long ICACHE_FLASH_ATTR +jsonparse_get_value_as_long(struct jsonparse_state *state) +{ + if(state->vtype != JSON_TYPE_NUMBER) { + return 0; + } + return atol(&state->json[state->vstart]); +} + +/*--------------------------------------------------------------------*/ +unsigned long ICACHE_FLASH_ATTR +jsonparse_get_value_as_ulong(struct jsonparse_state *state) +{ + if(state->vtype != JSON_TYPE_NUMBER) { + return 0; + } + return strtoul(&state->json[state->vstart], '\0', 0); +} + +/*--------------------------------------------------------------------*/ +/* strcmp - assume no strange chars that needs to be stuffed in string... */ +/*--------------------------------------------------------------------*/ +int ICACHE_FLASH_ATTR +jsonparse_strcmp_value(struct jsonparse_state *state, const char *str) +{ + if(state->vtype == 0) { + return -1; + } + return os_strncmp(str, &state->json[state->vstart], state->vlen); +} +/*--------------------------------------------------------------------*/ +int ICACHE_FLASH_ATTR +jsonparse_get_len(struct jsonparse_state *state) +{ + return state->vlen; +} +/*--------------------------------------------------------------------*/ +int ICACHE_FLASH_ATTR +jsonparse_get_type(struct jsonparse_state *state) +{ + if(state->depth == 0) { + return 0; + } + return state->stack[state->depth - 1]; +} +/*--------------------------------------------------------------------*/ +int ICACHE_FLASH_ATTR +jsonparse_has_next(struct jsonparse_state *state) +{ + return state->pos < state->len; +} +/*--------------------------------------------------------------------*/ +#endif + diff --git a/app/json/jsontree.c b/app/json/jsontree.c new file mode 100644 index 00000000..54445b84 --- /dev/null +++ b/app/json/jsontree.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * JSON output generation + * \author + * Niclas Finne + * Joakim Eriksson + */ +#ifdef JSON_FORMAT +//#include "contiki.h" +#include "json/jsontree.h" +#include "json/jsonparse.h" +#include "osapi.h" +//#include + +#define DEBUG 0 +#if DEBUG +//#include +#define PRINTF(...) os_printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +/*---------------------------------------------------------------------------*/ +void ICACHE_FLASH_ATTR +jsontree_write_atom(const struct jsontree_context *js_ctx, const char *text) +{ + if(text == NULL) { + js_ctx->putchar('0'); + } else { + while(*text != '\0') { + js_ctx->putchar(*text++); + } + } +} +/*---------------------------------------------------------------------------*/ +void ICACHE_FLASH_ATTR +jsontree_write_string(const struct jsontree_context *js_ctx, const char *text) +{ + js_ctx->putchar('"'); + if(text != NULL) { + while(*text != '\0') { + if(*text == '"') { + js_ctx->putchar('\\'); + } + js_ctx->putchar(*text++); + } + } + js_ctx->putchar('"'); +} +/*---------------------------------------------------------------------------*/ +void ICACHE_FLASH_ATTR +jsontree_write_int(const struct jsontree_context *js_ctx, int value) +{ + char buf[10]; + int l; + + if(value < 0) { + js_ctx->putchar('-'); + value = -value; + } + + l = sizeof(buf) - 1; + do { + buf[l--] = '0' + (value % 10); + value /= 10; + } while(value > 0 && l >= 0); + + while(++l < sizeof(buf)) { + js_ctx->putchar(buf[l]); + } +} + +/*---------------------------------------------------------------------------*/ +void ICACHE_FLASH_ATTR +jsontree_write_int_array(const struct jsontree_context *js_ctx, const int *text, uint32 length) +{ + uint32 i = 0; + if(text == NULL) { + js_ctx->putchar('0'); + } else { + for (i = 0; i < length - 1; i ++) { + jsontree_write_int(js_ctx, *text++); + js_ctx->putchar(','); + } + jsontree_write_int(js_ctx, *text); + } +} + + +/*---------------------------------------------------------------------------*/ +void ICACHE_FLASH_ATTR +jsontree_setup(struct jsontree_context *js_ctx, struct jsontree_value *root, + int (* putchar)(int)) +{ + js_ctx->values[0] = root; + js_ctx->putchar = putchar; + js_ctx->path = 0; + jsontree_reset(js_ctx); +} +/*---------------------------------------------------------------------------*/ +void ICACHE_FLASH_ATTR +jsontree_reset(struct jsontree_context *js_ctx) +{ + js_ctx->depth = 0; + js_ctx->index[0] = 0; +} +/*---------------------------------------------------------------------------*/ +const char *ICACHE_FLASH_ATTR +jsontree_path_name(const struct jsontree_context *js_ctx, int depth) +{ + if(depth < js_ctx->depth && js_ctx->values[depth]->type == JSON_TYPE_OBJECT) { + return ((struct jsontree_object *)js_ctx->values[depth])-> + pairs[js_ctx->index[depth]].name; + } + return ""; +} +/*---------------------------------------------------------------------------*/ +int ICACHE_FLASH_ATTR +jsontree_print_next(struct jsontree_context *js_ctx) +{ + struct jsontree_value *v; + int index; + + v = js_ctx->values[js_ctx->depth]; + + /* Default operation after switch is to back up one level */ + switch(v->type) { + case JSON_TYPE_OBJECT: + case JSON_TYPE_ARRAY: { + struct jsontree_array *o = (struct jsontree_array *)v; + struct jsontree_value *ov; + + index = js_ctx->index[js_ctx->depth]; + if(index == 0) { + js_ctx->putchar(v->type); + js_ctx->putchar('\n'); + } + if(index >= o->count) { + js_ctx->putchar('\n'); + js_ctx->putchar(v->type + 2); + /* Default operation: back up one level! */ + break; + } + + if(index > 0) { + js_ctx->putchar(','); + js_ctx->putchar('\n'); + } + if(v->type == JSON_TYPE_OBJECT) { + jsontree_write_string(js_ctx, + ((struct jsontree_object *)o)->pairs[index].name); + js_ctx->putchar(':'); + ov = ((struct jsontree_object *)o)->pairs[index].value; + } else { + ov = o->values[index]; + } + /* TODO check max depth */ + js_ctx->depth++; /* step down to value... */ + js_ctx->index[js_ctx->depth] = 0; /* and init index */ + js_ctx->values[js_ctx->depth] = ov; + /* Continue on this new level */ + return 1; + } + case JSON_TYPE_STRING: + jsontree_write_string(js_ctx, ((struct jsontree_string *)v)->value); + /* Default operation: back up one level! */ + break; + case JSON_TYPE_INT: + jsontree_write_int(js_ctx, ((struct jsontree_int *)v)->value); + /* Default operation: back up one level! */ + break; + case JSON_TYPE_CALLBACK: { /* pre-formatted json string currently */ + struct jsontree_callback *callback; + + callback = (struct jsontree_callback *)v; + if(js_ctx->index[js_ctx->depth] == 0) { + /* First call: reset the callback status */ + js_ctx->callback_state = 0; + } + if(callback->output == NULL) { + jsontree_write_string(js_ctx, ""); + } else if(callback->output(js_ctx)) { + /* The callback wants to output more */ + js_ctx->index[js_ctx->depth]++; + return 1; + } + /* Default operation: back up one level! */ + break; + } + default: + PRINTF("\nError: Illegal json type:'%c'\n", v->type); + return 0; + } + /* Done => back up one level! */ + if(js_ctx->depth > 0) { + js_ctx->depth--; + js_ctx->index[js_ctx->depth]++; + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static struct jsontree_value *ICACHE_FLASH_ATTR +find_next(struct jsontree_context *js_ctx) +{ + struct jsontree_value *v; + int index; + + do { + v = js_ctx->values[js_ctx->depth]; + + /* Default operation after switch is to back up one level */ + switch(v->type) { + case JSON_TYPE_OBJECT: + case JSON_TYPE_ARRAY: { + struct jsontree_array *o = (struct jsontree_array *)v; + struct jsontree_value *ov; + + index = js_ctx->index[js_ctx->depth]; + if(index >= o->count) { + /* Default operation: back up one level! */ + break; + } + + if(v->type == JSON_TYPE_OBJECT) { + ov = ((struct jsontree_object *)o)->pairs[index].value; + } else { + ov = o->values[index]; + } + /* TODO check max depth */ + js_ctx->depth++; /* step down to value... */ + js_ctx->index[js_ctx->depth] = 0; /* and init index */ + js_ctx->values[js_ctx->depth] = ov; + /* Continue on this new level */ + return ov; + } + default: + /* Default operation: back up one level! */ + break; + } + /* Done => back up one level! */ + if(js_ctx->depth > 0) { + js_ctx->depth--; + js_ctx->index[js_ctx->depth]++; + } else { + return NULL; + } + } while(1); +} +/*---------------------------------------------------------------------------*/ +struct jsontree_value *ICACHE_FLASH_ATTR +jsontree_find_next(struct jsontree_context *js_ctx, int type) +{ + struct jsontree_value *v; + + while((v = find_next(js_ctx)) != NULL && v->type != type && + js_ctx->path < js_ctx->depth) { + /* search */ + } + js_ctx->callback_state = 0; + return js_ctx->path < js_ctx->depth ? v : NULL; +} +/*---------------------------------------------------------------------------*/ +#endif + diff --git a/app/libc/Makefile b/app/libc/Makefile new file mode 100644 index 00000000..005f0857 --- /dev/null +++ b/app/libc/Makefile @@ -0,0 +1,44 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +GEN_LIBS = liblibc.a +endif + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ../wofs +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/libc/c_ctype.c b/app/libc/c_ctype.c new file mode 100644 index 00000000..a3683fd7 --- /dev/null +++ b/app/libc/c_ctype.c @@ -0,0 +1,16 @@ +#include "c_ctype.h" +#include "c_types.h" + +// int ICACHE_FLASH_ATTR isalnum(int c){} +// int ICACHE_FLASH_ATTR isalpha(int c){} +// int ICACHE_FLASH_ATTR iscntrl(int c){} +// int ICACHE_FLASH_ATTR isdigit(int c){} +// // int ICACHE_FLASH_ATTR isgraph(int c){} +// int ICACHE_FLASH_ATTR islower(int c){} +// int ICACHE_FLASH_ATTR isprint(int c){} +// int ICACHE_FLASH_ATTR ispunct(int c){} +// int ICACHE_FLASH_ATTR isspace(int c){} +// int ICACHE_FLASH_ATTR isupper(int c){} +// int ICACHE_FLASH_ATTR isxdigit(int c){} +// int ICACHE_FLASH_ATTR tolower(int c){} +// int ICACHE_FLASH_ATTR toupper(int c){} diff --git a/app/libc/c_ctype.h b/app/libc/c_ctype.h new file mode 100644 index 00000000..c7d443f2 --- /dev/null +++ b/app/libc/c_ctype.h @@ -0,0 +1,42 @@ +#ifndef _C_CTYPE_H_ +#define _C_CTYPE_H_ + +#if 0 +int isalnum(int); +int isalpha(int); +int iscntrl(int); +int isdigit(int); +// int isgraph(int); +int islower(int); +int isprint(int); +int ispunct(int); +int isspace(int); +int isupper(int); +int isxdigit(int); +int tolower(int); +int toupper(int); + +#if !defined(__STRICT_ANSI__) || defined(__cplusplus) || __STDC_VERSION__ >= 199901L +// int isblank(int); +#endif + +#ifndef __STRICT_ANSI__ +// int isascii(int); +// int toascii(int); +#define _tolower(__c) ((unsigned char)(__c) - 'A' + 'a') +#define _toupper(__c) ((unsigned char)(__c) - 'a' + 'A') +#endif + +#define _U 01 +#define _L 02 +#define _N 04 +#define _S 010 +#define _P 020 +#define _C 040 +#define _X 0100 +#define _B 0200 + +/* For C++ backward-compatibility only. */ +// extern char _ctype_[]; +#endif +#endif /* _C_CTYPE_H_ */ diff --git a/app/libc/c_errno.h b/app/libc/c_errno.h new file mode 100644 index 00000000..b0d79e44 --- /dev/null +++ b/app/libc/c_errno.h @@ -0,0 +1,19 @@ +#ifndef __c_errno_h +#define __c_errno_h + +#include +// #ifndef errno +// extern int errno; +// #endif + +// #define EDOM 1 +// #define ERANGE 2 +// #define EILSEQ 4 +// #define ESIGNUM 3 +// #define EINVAL 5 +// #define ENOMEM 6 + +#endif + +/* end of c_errno.h */ + diff --git a/app/libc/c_fcntl.h b/app/libc/c_fcntl.h new file mode 100644 index 00000000..84bba18e --- /dev/null +++ b/app/libc/c_fcntl.h @@ -0,0 +1,9 @@ +#ifndef __c_fcntl_h +#define __c_fcntl_h + +#include + +#endif + +/* end of c_fcntl.h */ + diff --git a/app/libc/c_limits.h b/app/libc/c_limits.h new file mode 100644 index 00000000..226a5bbc --- /dev/null +++ b/app/libc/c_limits.h @@ -0,0 +1,58 @@ +#ifndef __c_limits_h +#define __c_limits_h + +#include +#if 0 +#define CHAR_BIT 8 + /* max number of bits for smallest object that is not a bit-field (byte) */ +#define SCHAR_MIN (-128) + /* mimimum value for an object of type signed char */ +#define SCHAR_MAX 127 + /* maximum value for an object of type signed char */ +#define UCHAR_MAX 255 + /* maximum value for an object of type unsigned char */ +#ifdef __FEATURE_SIGNED_CHAR + #define CHAR_MIN (-128) + /* minimum value for an object of type char */ + #define CHAR_MAX 127 + /* maximum value for an object of type char */ +#else + #define CHAR_MIN 0 + /* minimum value for an object of type char */ + #define CHAR_MAX 255 + /* maximum value for an object of type char */ +#endif + +#define SHRT_MIN (-0x8000) + /* minimum value for an object of type short int */ +#define SHRT_MAX 0x7fff + /* maximum value for an object of type short int */ +#define USHRT_MAX 65535 + /* maximum value for an object of type unsigned short int */ +#define INT_MIN (~0x7fffffff) /* -2147483648 and 0x80000000 are unsigned */ + /* minimum value for an object of type int */ +#define INT_MAX 0x7fffffff + /* maximum value for an object of type int */ +#define UINT_MAX 0xffffffffU + /* maximum value for an object of type unsigned int */ +#define LONG_MIN (~0x7fffffffL) + /* minimum value for an object of type long int */ +#define LONG_MAX 0x7fffffffL + /* maximum value for an object of type long int */ +#define ULONG_MAX 0xffffffffUL + /* maximum value for an object of type unsigned long int */ +#if !defined(__STRICT_ANSI__) || (defined(__STDC_VERSION__) && 199901L <= __STDC_VERSION__) + #define LLONG_MIN (~0x7fffffffffffffffLL) + /* minimum value for an object of type long long int */ + #define LLONG_MAX 0x7fffffffffffffffLL + /* maximum value for an object of type long long int */ + #define ULLONG_MAX 0xffffffffffffffffULL + /* maximum value for an object of type unsigned long int */ +#endif + +#endif + +#endif + +/* end of c_limits.h */ + diff --git a/app/libc/c_locale.h b/app/libc/c_locale.h new file mode 100644 index 00000000..c26f59c2 --- /dev/null +++ b/app/libc/c_locale.h @@ -0,0 +1,62 @@ +/* + c_locale.h + Values appropriate for the formatting of monetary and other + numberic quantities. +*/ + +#ifndef _C_LOCALE_H_ +#define _C_LOCALE_H_ + +#include + +#if 0 +#ifndef NULL +#define NULL 0 +#endif + +#define LC_ALL 0 +#define LC_COLLATE 1 +#define LC_CTYPE 2 +#define LC_MONETARY 3 +#define LC_NUMERIC 4 +#define LC_TIME 5 +#define LC_MESSAGES 6 + +struct lconv +{ + char *decimal_point; + char *thousands_sep; + char *grouping; + char *int_curr_symbol; + char *currency_symbol; + char *mon_decimal_point; + char *mon_thousands_sep; + char *mon_grouping; + char *positive_sign; + char *negative_sign; + char int_frac_digits; + char frac_digits; + char p_cs_precedes; + char p_sep_by_space; + char n_cs_precedes; + char n_sep_by_space; + char p_sign_posn; + char n_sign_posn; + char int_n_cs_precedes; + char int_n_sep_by_space; + char int_n_sign_posn; + char int_p_cs_precedes; + char int_p_sep_by_space; + char int_p_sign_posn; +}; + +#ifndef _REENT_ONLY +// char *setlocale(int category, const char *locale); +struct lconv *localeconv(void); +#endif + +// struct _reent; +// char *_setlocale_r(struct _reent *, int category, const char *locale); +// struct lconv *_localeconv_r(struct _reent *); +#endif +#endif /* _C_LOCALE_H_ */ diff --git a/app/libc/c_math.c b/app/libc/c_math.c new file mode 100644 index 00000000..5e18a4d1 --- /dev/null +++ b/app/libc/c_math.c @@ -0,0 +1,80 @@ +#include "c_math.h" +#include "c_types.h" + +#if 0 +#ifndef __math_68881 +double ICACHE_FLASH_ATTR atan(double x){ + return x; +} +double ICACHE_FLASH_ATTR cos(double x){ + return x; +} +double ICACHE_FLASH_ATTR sin(double x){ + return x; +} +double ICACHE_FLASH_ATTR tan(double x){ + return x; +} +double ICACHE_FLASH_ATTR tanh(double x){ + return x; +} +double ICACHE_FLASH_ATTR frexp(double x, int *y){ + return x; +} +double ICACHE_FLASH_ATTR modf(double x, double *y){ + return x; +} +double ICACHE_FLASH_ATTR ceil(double x){ + return x; +} +double ICACHE_FLASH_ATTR fabs(double x){ + return x; +} +double ICACHE_FLASH_ATTR floor(double x){ + return x; +} +#endif /* ! defined (__math_68881) */ + +/* Non reentrant ANSI C functions. */ + +#ifndef _REENT_ONLY +#ifndef __math_68881 +double ICACHE_FLASH_ATTR acos(double x){ + return x; +} +double ICACHE_FLASH_ATTR asin(double x){ + return x; +} +double ICACHE_FLASH_ATTR atan2(double x, double y){ + return x; +} +double ICACHE_FLASH_ATTR cosh(double x){ + return x; +} +double ICACHE_FLASH_ATTR sinh(double x){ + return x; +} +double ICACHE_FLASH_ATTR exp(double x){ + return x; +} +double ICACHE_FLASH_ATTR ldexp(double x, int y){ + return x; +} +double ICACHE_FLASH_ATTR log(double x){ + return x; +} +double ICACHE_FLASH_ATTR log10(double x){ + return x; +} +double ICACHE_FLASH_ATTR pow(double x, double y){ + return x; +} +double ICACHE_FLASH_ATTR sqrt(double x){ + return x; +} +double ICACHE_FLASH_ATTR fmod(double x, double y){ + return x; +} +#endif /* ! defined (__math_68881) */ +#endif /* ! defined (_REENT_ONLY) */ +#endif diff --git a/app/libc/c_math.h b/app/libc/c_math.h new file mode 100644 index 00000000..65e31afe --- /dev/null +++ b/app/libc/c_math.h @@ -0,0 +1,58 @@ +#ifndef _C_MATH_H_ +#define _C_MATH_H_ +#include +#if 0 +#ifndef HUGE_VAL + #define HUGE_VAL (1.0e99) + #endif + + #ifndef HUGE_VALF + #define HUGE_VALF (1.0e999999999F) + #endif + + #if !defined(HUGE_VALL) && defined(_HAVE_LONG_DOUBLE) + #define HUGE_VALL (1.0e999999999L) + #endif + + #if !defined(INFINITY) + #define INFINITY (HUGE_VALF) + #endif + + +/* Reentrant ANSI C functions. */ + +#ifndef __math_68881 +// double atan(double); +// double cos(double); +// double sin(double); +// double tan(double); +// double tanh(double); +// double frexp(double, int *); +// double modf(double, double *); +// double ceil(double); +// double fabs(double); +// double floor(double); +#endif /* ! defined (__math_68881) */ + +/* Non reentrant ANSI C functions. */ + +#ifndef _REENT_ONLY +#ifndef __math_68881 +// double acos(double); +// double asin(double); +// double atan2(double, double); +// double cosh(double); +// double sinh(double); +// double exp(double); +// double ldexp(double, int); +// double log(double); +// double log10(double); +// double pow(double, double); +// double sqrt(double); +// double fmod(double, double); +#endif /* ! defined (__math_68881) */ +#endif /* ! defined (_REENT_ONLY) */ + +#endif + +#endif /* _MATH_H_ */ diff --git a/app/libc/c_signal.h b/app/libc/c_signal.h new file mode 100644 index 00000000..dab308f4 --- /dev/null +++ b/app/libc/c_signal.h @@ -0,0 +1,6 @@ +#ifndef _C_SIGNAL_H_ +#define _C_SIGNAL_H_ + +#include + +#endif /* _C_SIGNAL_H_ */ diff --git a/app/libc/c_stdarg.h b/app/libc/c_stdarg.h new file mode 100644 index 00000000..ed1222c9 --- /dev/null +++ b/app/libc/c_stdarg.h @@ -0,0 +1,14 @@ +#ifndef __c_stdarg_h +#define __c_stdarg_h + +typedef char * va_list; + +#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)) + +#define va_start(ap,v) (ap = (va_list)&v + _INTSIZEOF(v)) +#define va_arg(ap,t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) +#define va_end(ap) (ap = (va_list)0) + +#endif + +/* end of c_stdarg.h */ diff --git a/app/libc/c_stddef.h b/app/libc/c_stddef.h new file mode 100644 index 00000000..5e969547 --- /dev/null +++ b/app/libc/c_stddef.h @@ -0,0 +1,23 @@ +#ifndef __c_stddef_h +#define __c_stddef_h + +typedef signed int ptrdiff_t; + +#if !defined(offsetof) +#define offsetof(s, m) (size_t)&(((s *)0)->m) +#endif + +#if !defined(__size_t) + #define __size_t 1 + typedef unsigned int size_t; /* others (e.g. ) also define */ + /* the unsigned integral type of the result of the sizeof operator. */ +#endif + +#undef NULL /* others (e.g. ) also define */ +#define NULL 0 + /* null pointer constant. */ + +#endif + +/* end of c_stddef.h */ + diff --git a/app/libc/c_stdint.h b/app/libc/c_stdint.h new file mode 100644 index 00000000..ba7fffd4 --- /dev/null +++ b/app/libc/c_stdint.h @@ -0,0 +1,273 @@ +#ifndef __c_stdint_h +#define __c_stdint_h + +#include "c_types.h" +#if 0 +/* + * Depending on compiler version __int64 or __INT64_TYPE__ should be defined. + */ +#ifndef __int64 + #ifdef __INT64_TYPE__ + #define __int64 __INT64_TYPE__ + #else + #define __int64 long long + #endif + /* On some architectures neither of these may be defined - if so, fall + through and error out if used. */ +#endif + + #ifndef __STDINT_DECLS + #define __STDINT_DECLS + + #undef __CLIBNS + + #ifdef __cplusplus + namespace std { + #define __CLIBNS std:: + extern "C" { + #else + #define __CLIBNS + #endif /* __cplusplus */ + + +/* + * 'signed' is redundant below, except for 'signed char' and if + * the typedef is used to declare a bitfield. + * '__int64' is used instead of 'long long' so that this header + * can be used in --strict mode. + */ + + /* 7.18.1.1 */ + + /* exact-width signed integer types */ +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed __int64 int64_t; + + /* exact-width unsigned integer types */ +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned __int64 uint64_t; + + /* 7.18.1.2 */ + + /* smallest type of at least n bits */ + /* minimum-width signed integer types */ +typedef signed char int_least8_t; +typedef signed short int int_least16_t; +typedef signed int int_least32_t; +typedef signed __int64 int_least64_t; + + /* minimum-width unsigned integer types */ +typedef unsigned char uint_least8_t; +typedef unsigned short int uint_least16_t; +typedef unsigned int uint_least32_t; +typedef unsigned __int64 uint_least64_t; + + /* 7.18.1.3 */ + + /* fastest minimum-width signed integer types */ +typedef signed int int_fast8_t; +typedef signed int int_fast16_t; +typedef signed int int_fast32_t; +typedef signed __int64 int_fast64_t; + + /* fastest minimum-width unsigned integer types */ +typedef unsigned int uint_fast8_t; +typedef unsigned int uint_fast16_t; +typedef unsigned int uint_fast32_t; +typedef unsigned __int64 uint_fast64_t; + + /* 7.18.1.4 integer types capable of holding object pointers */ +typedef signed int intptr_t; +typedef unsigned int uintptr_t; + + /* 7.18.1.5 greatest-width integer types */ +typedef signed __int64 intmax_t; +typedef unsigned __int64 uintmax_t; + + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) + + /* 7.18.2.1 */ + + /* minimum values of exact-width signed integer types */ +#define INT8_MIN -128 +#define INT16_MIN -32768 +#define INT32_MIN (~0x7fffffff) /* -2147483648 is unsigned */ +#define INT64_MIN __ESCAPE__(~0x7fffffffffffffffll) /* -9223372036854775808 is unsigned */ + + /* maximum values of exact-width signed integer types */ +#define INT8_MAX 127 +#define INT16_MAX 32767 +#define INT32_MAX 2147483647 +#define INT64_MAX __ESCAPE__(9223372036854775807ll) + + /* maximum values of exact-width unsigned integer types */ +#define UINT8_MAX 255 +#define UINT16_MAX 65535 +#define UINT32_MAX 4294967295u +#define UINT64_MAX __ESCAPE__(18446744073709551615ull) + + /* 7.18.2.2 */ + + /* minimum values of minimum-width signed integer types */ +#define INT_LEAST8_MIN -128 +#define INT_LEAST16_MIN -32768 +#define INT_LEAST32_MIN (~0x7fffffff) +#define INT_LEAST64_MIN __ESCAPE__(~0x7fffffffffffffffll) + + /* maximum values of minimum-width signed integer types */ +#define INT_LEAST8_MAX 127 +#define INT_LEAST16_MAX 32767 +#define INT_LEAST32_MAX 2147483647 +#define INT_LEAST64_MAX __ESCAPE__(9223372036854775807ll) + + /* maximum values of minimum-width unsigned integer types */ +#define UINT_LEAST8_MAX 255 +#define UINT_LEAST16_MAX 65535 +#define UINT_LEAST32_MAX 4294967295u +#define UINT_LEAST64_MAX __ESCAPE__(18446744073709551615ull) + + /* 7.18.2.3 */ + + /* minimum values of fastest minimum-width signed integer types */ +#define INT_FAST8_MIN (~0x7fffffff) +#define INT_FAST16_MIN (~0x7fffffff) +#define INT_FAST32_MIN (~0x7fffffff) +#define INT_FAST64_MIN __ESCAPE__(~0x7fffffffffffffffll) + + /* maximum values of fastest minimum-width signed integer types */ +#define INT_FAST8_MAX 2147483647 +#define INT_FAST16_MAX 2147483647 +#define INT_FAST32_MAX 2147483647 +#define INT_FAST64_MAX __ESCAPE__(9223372036854775807ll) + + /* maximum values of fastest minimum-width unsigned integer types */ +#define UINT_FAST8_MAX 4294967295u +#define UINT_FAST16_MAX 4294967295u +#define UINT_FAST32_MAX 4294967295u +#define UINT_FAST64_MAX __ESCAPE__(18446744073709551615ull) + + /* 7.18.2.4 */ + + /* minimum value of pointer-holding signed integer type */ +#define INTPTR_MIN (~0x7fffffff) + + /* maximum value of pointer-holding signed integer type */ +#define INTPTR_MAX 2147483647 + + /* maximum value of pointer-holding unsigned integer type */ +#define UINTPTR_MAX 4294967295u + + /* 7.18.2.5 */ + + /* minimum value of greatest-width signed integer type */ +#define INTMAX_MIN __ESCAPE__(~0x7fffffffffffffffll) + + /* maximum value of greatest-width signed integer type */ +#define INTMAX_MAX __ESCAPE__(9223372036854775807ll) + + /* maximum value of greatest-width unsigned integer type */ +#define UINTMAX_MAX __ESCAPE__(18446744073709551615ull) + + /* 7.18.3 */ + + /* limits of ptrdiff_t */ +#define PTRDIFF_MIN (~0x7fffffff) +#define PTRDIFF_MAX 2147483647 + + /* limits of sig_atomic_t */ +#define SIG_ATOMIC_MIN (~0x7fffffff) +#define SIG_ATOMIC_MAX 2147483647 + + /* limit of size_t */ +#define SIZE_MAX 4294967295u + + /* limits of wchar_t */ + /* NB we have to undef and redef because they're defined in both + * stdint.h and wchar.h */ +#undef WCHAR_MIN +#undef WCHAR_MAX + +#if defined(__WCHAR32) || (defined(__ARM_SIZEOF_WCHAR_T) && __ARM_SIZEOF_WCHAR_T == 4) + #define WCHAR_MIN 0 + #define WCHAR_MAX 0xffffffffU +#else + #define WCHAR_MIN 0 + #define WCHAR_MAX 65535 +#endif + + /* limits of wint_t */ +#define WINT_MIN (~0x7fffffff) +#define WINT_MAX 2147483647 + +#endif /* __STDC_LIMIT_MACROS */ + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) + + /* 7.18.4.1 macros for minimum-width integer constants */ +#define INT8_C(x) (x) +#define INT16_C(x) (x) +#define INT32_C(x) (x) +#define INT64_C(x) __ESCAPE__(x ## ll) + +#define UINT8_C(x) (x ## u) +#define UINT16_C(x) (x ## u) +#define UINT32_C(x) (x ## u) +#define UINT64_C(x) __ESCAPE__(x ## ull) + + /* 7.18.4.2 macros for greatest-width integer constants */ +#define INTMAX_C(x) __ESCAPE__(x ## ll) +#define UINTMAX_C(x) __ESCAPE__(x ## ull) + +#endif /* __STDC_CONSTANT_MACROS */ + + #ifdef __cplusplus + } /* extern "C" */ + } /* namespace std */ + #endif /* __cplusplus */ + #endif /* __STDINT_DECLS */ + + #ifdef __cplusplus + #ifndef __STDINT_NO_EXPORTS + using ::std::int8_t; + using ::std::int16_t; + using ::std::int32_t; + using ::std::int64_t; + using ::std::uint8_t; + using ::std::uint16_t; + using ::std::uint32_t; + using ::std::uint64_t; + using ::std::int_least8_t; + using ::std::int_least16_t; + using ::std::int_least32_t; + using ::std::int_least64_t; + using ::std::uint_least8_t; + using ::std::uint_least16_t; + using ::std::uint_least32_t; + using ::std::uint_least64_t; + using ::std::int_fast8_t; + using ::std::int_fast16_t; + using ::std::int_fast32_t; + using ::std::int_fast64_t; + using ::std::uint_fast8_t; + using ::std::uint_fast16_t; + using ::std::uint_fast32_t; + using ::std::uint_fast64_t; + using ::std::intptr_t; + using ::std::uintptr_t; + using ::std::intmax_t; + using ::std::uintmax_t; + #endif + #endif /* __cplusplus */ +#endif + +#endif /* __c_stdint_h */ + +/* end of c_stdint.h */ + + + diff --git a/app/libc/c_stdio.c b/app/libc/c_stdio.c new file mode 100644 index 00000000..88c135b1 --- /dev/null +++ b/app/libc/c_stdio.c @@ -0,0 +1,56 @@ +#include "c_stdio.h" +// #include "driver/uart.h" + +unsigned char __print_buf[BUFSIZ]; +int c_stdin = 999; +int c_stdout = 1000; +int c_stderr = 1001; + +// FILE *c_fopen(const char *_name, const char *_type){ +// } +// FILE *c_freopen(const char *_name, const char *_type, FILE *_f){ +// } +// FILE *c_tmpfile(void){ +// } + +// int c_putchar(int c){ +// } + +// int c_printf(const char *c, ...){ +// } + +// int c_sprintf(char *c, const char *s, ...){ +// } + +// int c_fprintf(FILE *f, const char *s, ...){ +// } +// int c_fscanf(FILE *f, const char *s, ...){ +// } +// int c_fclose(FILE *f){ +// } +// int c_fflush(FILE *f){ +// } +// int c_setvbuf(FILE *f, char *c, int d, size_t t){ +// } +// void c_clearerr(FILE *f){ +// } +// int c_fseek(FILE *f, long l, int d){ +// } +// long c_ftell( FILE *f){ +// } +// int c_fputs(const char *c, FILE *f){ +// } +// char *c_fgets(char *c, int d, FILE *f){ +// } +// int c_ungetc(int d, FILE *f){ +// } +// size_t c_fread(void *p, size_t _size, size_t _n, FILE *f){ +// } +// size_t c_fwrite(const void *p, size_t _size, size_t _n, FILE *f){ +// } +// int c_feof(FILE *f){ +// } +// int c_ferror(FILE *f){ +// } +// int c_getc(FILE *f){ +// } diff --git a/app/libc/c_stdio.h b/app/libc/c_stdio.h new file mode 100644 index 00000000..d08effbf --- /dev/null +++ b/app/libc/c_stdio.h @@ -0,0 +1,92 @@ +#ifndef _C_STDIO_H_ +#define _C_STDIO_H_ + +#define __need_size_t + +#include "c_stddef.h" +#include "osapi.h" +// #include "driver/uart.h" + +// #define __need___va_list +//#include "c_stdarg.h" + +//struct __sFILE{ +// int _r; /* read space left for getc() */ +// int _w; /* write space left for putc() */ +//}; +// typedef struct __sFILE __FILE; +// typedef __FILE FILE; + +extern int c_stdin; +extern int c_stdout; +extern int c_stderr; + +// #define _IOFBF 0 /* setvbuf should set fully buffered */ +// #define _IOLBF 1 /* setvbuf should set line buffered */ +// #define _IONBF 2 /* setvbuf should set unbuffered */ + +// #ifndef NULL +// #define NULL 0 +// #endif + +#define EOF (-1) + +#ifdef __BUFSIZ__ +#define BUFSIZ __BUFSIZ__ +#else +#define BUFSIZ 1024 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 /* set file offset to offset */ +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 /* set file offset to current plus offset */ +#endif +#ifndef SEEK_END +#define SEEK_END 2 /* set file offset to EOF plus offset */ +#endif + +extern void output_redirect(const char *str); +#define c_puts output_redirect +extern unsigned char __print_buf[BUFSIZ]; +// #define c_printf os_printf +// int c_printf(const char *c, ...); +#define c_sprintf os_sprintf +// #define c_vsprintf ets_vsprintf +#define c_printf(...) do { \ + c_sprintf(__print_buf, __VA_ARGS__); \ + c_puts(__print_buf); \ +} while(0) + +// #define c_getc ets_getc +// #define c_getchar ets_getc +// note: contact esp to ensure the real getchar function.. + +// FILE *c_fopen(const char *_name, const char *_type); +// FILE *c_freopen(const char *, const char *, FILE *); +// FILE *c_tmpfile(void); + +// int c_putchar(int); +// int c_printf(const char *, ...); +// int c_sprintf(char *, const char *, ...); +// int c_getc(FILE *); + +// int c_ungetc(int, FILE *); + +// int c_fprintf(FILE *, const char *, ...); +// int c_fscanf(FILE *, const char *, ...); +// int c_fclose(FILE *); +// int c_fflush(FILE *); +// int c_setvbuf(FILE *, char *, int, size_t); +// void c_clearerr(FILE *); +// int c_fseek(FILE *, long, int); +// long c_ftell( FILE *); +// int c_fputs(const char *, FILE *); +// char *c_fgets(char *, int, FILE *); +// size_t c_fread(void *, size_t _size, size_t _n, FILE *); +// size_t c_fwrite(const void * , size_t _size, size_t _n, FILE *); +// int c_feof(FILE *); +// int c_ferror(FILE *); + +#endif /* _C_STDIO_H_ */ diff --git a/app/libc/c_stdlib.c b/app/libc/c_stdlib.c new file mode 100644 index 00000000..e1598392 --- /dev/null +++ b/app/libc/c_stdlib.c @@ -0,0 +1,60 @@ +#include "c_stdlib.h" +#include "c_stdio.h" +#include "c_types.h" +#include "c_string.h" +#include "user_interface.h" + +// const char *lua_init_value = "print(\"Hello world\")"; +const char *lua_init_value = "@init.lua"; + +// int ICACHE_FLASH_ATTR c_abs(int x){ +// return x>0?x:0-x; +// } +// void ICACHE_FLASH_ATTR c_exit(int e){ +// } +const char *ICACHE_FLASH_ATTR c_getenv(const char *__string){ + if (c_strcmp(__string, "LUA_INIT") == 0){ + return lua_init_value; + } + return NULL; +} + +// make sure there is enough memory before real malloc, otherwise malloc will panic and reset +void *ICACHE_FLASH_ATTR c_malloc(size_t __size){ + if(__size>system_get_free_heap_size()){ + NODE_ERR("malloc: not enough memory\n"); + return NULL; + } + return (void *)os_malloc(__size); +} + +void *ICACHE_FLASH_ATTR c_zalloc(size_t __size){ + if(__size>system_get_free_heap_size()){ + NODE_ERR("zalloc: not enough memory\n"); + return NULL; + } + return (void *)os_zalloc(__size); +} + +void ICACHE_FLASH_ATTR c_free(void *p){ + // NODE_ERR("free1: %d\n", system_get_free_heap_size()); + os_free(p); + // NODE_ERR("-free1: %d\n", system_get_free_heap_size()); +} + + +// int ICACHE_FLASH_ATTR c_rand(void){ +// } +// void ICACHE_FLASH_ATTR c_srand(unsigned int __seed){ +// } + +// int ICACHE_FLASH_ATTR c_atoi(const char *__nptr){ +// } +// double ICACHE_FLASH_ATTR c_strtod(const char *__n, char **__end_PTR){ +// } +// long ICACHE_FLASH_ATTR c_strtol(const char *__n, char **__end_PTR, int __base){ +// } +// unsigned long ICACHE_FLASH_ATTR c_strtoul(const char *__n, char **__end_PTR, int __base){ +// } +// long long ICACHE_FLASH_ATTR c_strtoll(const char *__n, char **__end_PTR, int __base){ +// } diff --git a/app/libc/c_stdlib.h b/app/libc/c_stdlib.h new file mode 100644 index 00000000..c290ee2e --- /dev/null +++ b/app/libc/c_stdlib.h @@ -0,0 +1,63 @@ +/* + * c_stdlib.h + * + * Definitions for common types, variables, and functions. + */ + +#ifndef _C_STDLIB_H_ +#define _C_STDLIB_H_ + +#include "c_stddef.h" +#include "mem.h" + +#define EXIT_FAILURE 1 +#define EXIT_SUCCESS 0 + +#define __INT_MAX__ 2147483647 +#undef __RAND_MAX +#if __INT_MAX__ == 32767 +#define __RAND_MAX 32767 +#else +#define __RAND_MAX 0x7fffffff +#endif +#define RAND_MAX __RAND_MAX + +#ifndef mem_realloc +#define mem_realloc pvPortRealloc +#endif +#ifndef os_realloc +#define os_realloc(p, s) mem_realloc((p), (s)) +#endif + +// #define c_free os_free +// #define c_malloc os_malloc +// #define c_zalloc os_zalloc +#define c_realloc os_realloc + +#define c_abs abs +#define c_atoi atoi +// #define c_strtod strtod +#define c_strtol strtol +#define c_strtoul strtoul + +// int c_abs(int); + +// void c_exit(int); + +// c_getenv() get env "LUA_INIT" string for lua initialization. +const char *c_getenv(const char *__string); + +void *c_malloc(size_t __size); +void *c_zalloc(size_t __size); +void c_free(void *); + +// int c_rand(void); +// void c_srand(unsigned int __seed); + +// int c_atoi(const char *__nptr); +// double c_strtod(const char *__n, char **__end_PTR); +// // long c_strtol(const char *__n, char **__end_PTR, int __base); +// unsigned long c_strtoul(const char *__n, char **__end_PTR, int __base); +// // long long c_strtoll(const char *__n, char **__end_PTR, int __base); + +#endif /* _C_STDLIB_H_ */ diff --git a/app/libc/c_string.c b/app/libc/c_string.c new file mode 100644 index 00000000..69ce067c --- /dev/null +++ b/app/libc/c_string.c @@ -0,0 +1,16 @@ +#include "c_string.h" + +// const char *c_strstr(const char * __s1, const char * __s2){ +// } + +// char *c_strncat(char * s1, const char * s2, size_t n){ +// } + +// size_t c_strcspn(const char * s1, const char * s2){ +// } + +// const char *c_strpbrk(const char * s1, const char * s2){ +// } + +// int c_strcoll(const char * s1, const char * s2){ +// } diff --git a/app/libc/c_string.h b/app/libc/c_string.h new file mode 100644 index 00000000..599ac4c8 --- /dev/null +++ b/app/libc/c_string.h @@ -0,0 +1,43 @@ +/* + * c_string.h + * + * Definitions for memory and string functions. + */ + +#ifndef _C_STRING_H_ +#define _C_STRING_H_ +#include "c_stddef.h" +#include "osapi.h" + +#ifndef NULL +#define NULL 0 +#endif + +#define c_memcmp os_memcmp +#define c_memcpy os_memcpy +#define c_memset os_memset + +#define c_strcat os_strcat +#define c_strchr os_strchr +#define c_strcmp os_strcmp +#define c_strcpy os_strcpy +#define c_strlen os_strlen +#define c_strncmp os_strncmp +#define c_strncpy os_strncpy +// #define c_strstr os_strstr +#define c_strncasecmp c_strcmp + +#define c_strstr strstr +#define c_strncat strncat +#define c_strcspn strcspn +#define c_strpbrk strpbrk +#define c_strcoll strcoll +#define c_strrchr strrchr + +// const char *c_strstr(const char * __s1, const char * __s2); +// char *c_strncat(char * __restrict /*s1*/, const char * __restrict /*s2*/, size_t n); +// size_t c_strcspn(const char * s1, const char * s2); +// const char *c_strpbrk(const char * /*s1*/, const char * /*s2*/); +// int c_strcoll(const char * /*s1*/, const char * /*s2*/); + +#endif /* _C_STRING_H_ */ diff --git a/app/lua/Makefile b/app/lua/Makefile new file mode 100644 index 00000000..93d1f67b --- /dev/null +++ b/app/lua/Makefile @@ -0,0 +1,48 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +GEN_LIBS = liblua.a +endif + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ../wofs +INCLUDES += -I ../spiffs +INCLUDES += -I ../libc +INCLUDES += -I ../modules +INCLUDES += -I ../platform +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/lua/compiler.h b/app/lua/compiler.h new file mode 100644 index 00000000..6b36f88d --- /dev/null +++ b/app/lua/compiler.h @@ -0,0 +1,47 @@ +/** + * define start/end address of ro data. + * different compiler with different implementation. + */ + +#ifndef __COMPILER_H__ +#define __COMPILER_H__ + +#if defined(__CC_ARM) // armcc + +//#warning "Please check scatter file to ensure rodata is in ER_IROM1 region." + +/* symbols reference to the scatter file */ +extern char Image$$ER_IROM1$$Base; +extern char Image$$ER_IROM1$$Limit; + +#define RODATA_START_ADDRESS (&Image$$ER_IROM1$$Base) +#define RODATA_END_ADDRESS (&Image$$ER_IROM1$$Limit) + +#elif defined(__GNUC__) // gcc + +//#warning "Please check linker script to ensure rodata is between _stext and _etext." + +/* symbols defined in linker script */ +extern char _rodata_start; +extern char _rodata_end; +// extern char _irom0_text_start; +// extern char _irom0_text_end; +// modify linker script to ensure rodata and rodata1 is between _rodata_start and _rodata_end. +#define RODATA_START_ADDRESS (&_rodata_start) +#define RODATA_END_ADDRESS (&_rodata_end) +// #define RODATA_START_ADDRESS (&_irom0_text_start) +// #define RODATA_END_ADDRESS (&_irom0_text_end) + +#else // other compilers + +/* Firstly, modify rodata's start/end address. Then, comment the line below */ +#error "Please modify RODATA_START_ADDRESS and RODATA_END_ADDRESS below." + +/* Perhaps you can use start/end address of flash */ +#define RODATA_START_ADDRESS ((char*)0x40200000) +#define RODATA_END_ADDRESS ((char*)0x40280000) + +#endif + +#endif // __COMPILER_H__ + diff --git a/app/lua/lapi.c b/app/lua/lapi.c new file mode 100644 index 00000000..2e76a4d7 --- /dev/null +++ b/app/lua/lapi.c @@ -0,0 +1,1161 @@ +/* +** $Id: lapi.c,v 2.55.1.5 2008/07/04 18:41:18 roberto Exp $ +** Lua API +** See Copyright Notice in lua.h +*/ + + +//#include "c_assert.h" +#include "c_math.h" +#include "c_stdarg.h" +#include "c_string.h" + +#define lapi_c +#define LUA_CORE + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" +#include "lrotable.h" + +#if 0 +const char lua_ident[] = + "$Lua: " LUA_RELEASE " " LUA_COPYRIGHT " $\n" + "$Authors: " LUA_AUTHORS " $\n" + "$URL: www.lua.org $\n"; +#endif + + +#define api_checknelems(L, n) api_check(L, (n) <= (L->top - L->base)) + +#define api_checkvalidindex(L, i) api_check(L, (i) != luaO_nilobject) + +#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;} + + + +static TValue *ICACHE_FLASH_ATTR index2adr (lua_State *L, int idx) { + if (idx > 0) { + TValue *o = L->base + (idx - 1); + api_check(L, idx <= L->ci->top - L->base); + if (o >= L->top) return cast(TValue *, luaO_nilobject); + else return o; + } + else if (idx > LUA_REGISTRYINDEX) { + api_check(L, idx != 0 && -idx <= L->top - L->base); + return L->top + idx; + } + else switch (idx) { /* pseudo-indices */ + case LUA_REGISTRYINDEX: return registry(L); + case LUA_ENVIRONINDEX: { + Closure *func = curr_func(L); + sethvalue(L, &L->env, func ? func->c.env : hvalue(gt(L))); + return &L->env; + } + case LUA_GLOBALSINDEX: return gt(L); + default: { + Closure *func = curr_func(L); + if (!func) return cast(TValue *, luaO_nilobject); + idx = LUA_GLOBALSINDEX - idx; + return (idx <= func->c.nupvalues) + ? &func->c.upvalue[idx-1] + : cast(TValue *, luaO_nilobject); + } + } +} + + +static Table *ICACHE_FLASH_ATTR getcurrenv (lua_State *L) { + if (L->ci == L->base_ci) /* no enclosing function? */ + return hvalue(gt(L)); /* use global table as environment */ + else { + Closure *func = curr_func(L); + return func ? func->c.env : hvalue(gt(L)); + } +} + + +void ICACHE_FLASH_ATTR luaA_pushobject (lua_State *L, const TValue *o) { + setobj2s(L, L->top, o); + api_incr_top(L); +} + + +LUA_API int ICACHE_FLASH_ATTR lua_checkstack (lua_State *L, int size) { + int res = 1; + lua_lock(L); + if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK) + res = 0; /* stack overflow */ + else if (size > 0) { + luaD_checkstack(L, size); + if (L->ci->top < L->top + size) + L->ci->top = L->top + size; + } + lua_unlock(L); + return res; +} + + +LUA_API void ICACHE_FLASH_ATTR lua_xmove (lua_State *from, lua_State *to, int n) { + int i; + if (from == to) return; + lua_lock(to); + api_checknelems(from, n); + api_check(from, G(from) == G(to)); + api_check(from, to->ci->top - to->top >= n); + from->top -= n; + for (i = 0; i < n; i++) { + setobj2s(to, to->top++, from->top + i); + } + lua_unlock(to); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_setlevel (lua_State *from, lua_State *to) { + to->nCcalls = from->nCcalls; +} + + +LUA_API lua_CFunction ICACHE_FLASH_ATTR lua_atpanic (lua_State *L, lua_CFunction panicf) { + lua_CFunction old; + lua_lock(L); + old = G(L)->panic; + G(L)->panic = panicf; + lua_unlock(L); + return old; +} + + +LUA_API lua_State *ICACHE_FLASH_ATTR lua_newthread (lua_State *L) { + lua_State *L1; + lua_lock(L); + luaC_checkGC(L); + L1 = luaE_newthread(L); + setthvalue(L, L->top, L1); + api_incr_top(L); + lua_unlock(L); + luai_userstatethread(L, L1); + return L1; +} + + + +/* +** basic stack manipulation +*/ + + +LUA_API int ICACHE_FLASH_ATTR lua_gettop (lua_State *L) { + return cast_int(L->top - L->base); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_settop (lua_State *L, int idx) { + lua_lock(L); + if (idx >= 0) { + api_check(L, idx <= L->stack_last - L->base); + while (L->top < L->base + idx) + setnilvalue(L->top++); + L->top = L->base + idx; + } + else { + api_check(L, -(idx+1) <= (L->top - L->base)); + L->top += idx+1; /* `subtract' index (index is negative) */ + } + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_remove (lua_State *L, int idx) { + StkId p; + lua_lock(L); + p = index2adr(L, idx); + api_checkvalidindex(L, p); + while (++p < L->top) setobjs2s(L, p-1, p); + L->top--; + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_insert (lua_State *L, int idx) { + StkId p; + StkId q; + lua_lock(L); + p = index2adr(L, idx); + api_checkvalidindex(L, p); + for (q = L->top; q>p; q--) setobjs2s(L, q, q-1); + setobjs2s(L, p, L->top); + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_replace (lua_State *L, int idx) { + StkId o; + lua_lock(L); + /* explicit test for incompatible code */ + if (idx == LUA_ENVIRONINDEX && L->ci == L->base_ci) + luaG_runerror(L, "no calling environment"); + api_checknelems(L, 1); + o = index2adr(L, idx); + api_checkvalidindex(L, o); + if (idx == LUA_ENVIRONINDEX) { + Closure *func = curr_func(L); + if (!func) + luaG_runerror(L, "attempt to set environment on lightfunction"); + else { + api_check(L, ttistable(L->top - 1)); + func->c.env = hvalue(L->top - 1); + luaC_barrier(L, func, L->top - 1); + } + } + else { + setobj(L, o, L->top - 1); + if (curr_func(L) && idx < LUA_GLOBALSINDEX) /* function upvalue? */ + luaC_barrier(L, curr_func(L), L->top - 1); + } + L->top--; + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_pushvalue (lua_State *L, int idx) { + lua_lock(L); + setobj2s(L, L->top, index2adr(L, idx)); + api_incr_top(L); + lua_unlock(L); +} + + + +/* +** access functions (stack -> C) +*/ + + +LUA_API int ICACHE_FLASH_ATTR lua_type (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return (o == luaO_nilobject) ? LUA_TNONE : ttype(o); +} + + +LUA_API const char *ICACHE_FLASH_ATTR lua_typename (lua_State *L, int t) { + UNUSED(L); + return (t == LUA_TNONE) ? "no value" : luaT_typenames[t]; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_iscfunction (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return iscfunction(o); +} + + +LUA_API int ICACHE_FLASH_ATTR lua_isnumber (lua_State *L, int idx) { + TValue n; + const TValue *o = index2adr(L, idx); + return tonumber(o, &n); +} + + +LUA_API int ICACHE_FLASH_ATTR lua_isstring (lua_State *L, int idx) { + int t = lua_type(L, idx); + return (t == LUA_TSTRING || t == LUA_TNUMBER); +} + + +LUA_API int ICACHE_FLASH_ATTR lua_isuserdata (lua_State *L, int idx) { + const TValue *o = index2adr(L, idx); + return (ttisuserdata(o) || ttislightuserdata(o)); +} + + +LUA_API int ICACHE_FLASH_ATTR lua_rawequal (lua_State *L, int index1, int index2) { + StkId o1 = index2adr(L, index1); + StkId o2 = index2adr(L, index2); + return (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 + : luaO_rawequalObj(o1, o2); +} + + +LUA_API int ICACHE_FLASH_ATTR lua_equal (lua_State *L, int index1, int index2) { + StkId o1, o2; + int i; + lua_lock(L); /* may call tag method */ + o1 = index2adr(L, index1); + o2 = index2adr(L, index2); + i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : equalobj(L, o1, o2); + lua_unlock(L); + return i; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_lessthan (lua_State *L, int index1, int index2) { + StkId o1, o2; + int i; + lua_lock(L); /* may call tag method */ + o1 = index2adr(L, index1); + o2 = index2adr(L, index2); + i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 + : luaV_lessthan(L, o1, o2); + lua_unlock(L); + return i; +} + + + +LUA_API lua_Number ICACHE_FLASH_ATTR lua_tonumber (lua_State *L, int idx) { + TValue n; + const TValue *o = index2adr(L, idx); + if (tonumber(o, &n)) + return nvalue(o); + else + return 0; +} + + +LUA_API lua_Integer ICACHE_FLASH_ATTR lua_tointeger (lua_State *L, int idx) { + TValue n; + const TValue *o = index2adr(L, idx); + if (tonumber(o, &n)) { + lua_Integer res; + lua_Number num = nvalue(o); + lua_number2integer(res, num); + return res; + } + else + return 0; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_toboolean (lua_State *L, int idx) { + const TValue *o = index2adr(L, idx); + return !l_isfalse(o); +} + + +LUA_API const char *ICACHE_FLASH_ATTR lua_tolstring (lua_State *L, int idx, size_t *len) { + StkId o = index2adr(L, idx); + if (!ttisstring(o)) { + lua_lock(L); /* `luaV_tostring' may create a new string */ + if (!luaV_tostring(L, o)) { /* conversion failed? */ + if (len != NULL) *len = 0; + lua_unlock(L); + return NULL; + } + luaC_checkGC(L); + o = index2adr(L, idx); /* previous call may reallocate the stack */ + lua_unlock(L); + } + if (len != NULL) *len = tsvalue(o)->len; + return svalue(o); +} + + +LUA_API size_t ICACHE_FLASH_ATTR lua_objlen (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + switch (ttype(o)) { + case LUA_TSTRING: return tsvalue(o)->len; + case LUA_TUSERDATA: return uvalue(o)->len; + case LUA_TTABLE: return luaH_getn(hvalue(o)); + case LUA_TROTABLE: return luaH_getn_ro(rvalue(o)); + case LUA_TNUMBER: { + size_t l; + lua_lock(L); /* `luaV_tostring' may create a new string */ + l = (luaV_tostring(L, o) ? tsvalue(o)->len : 0); + lua_unlock(L); + return l; + } + default: return 0; + } +} + + +LUA_API lua_CFunction ICACHE_FLASH_ATTR lua_tocfunction (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return (!iscfunction(o)) ? NULL : clvalue(o)->c.f; +} + + +LUA_API void *ICACHE_FLASH_ATTR lua_touserdata (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + switch (ttype(o)) { + case LUA_TUSERDATA: return (rawuvalue(o) + 1); + case LUA_TLIGHTUSERDATA: return pvalue(o); + default: return NULL; + } +} + + +LUA_API lua_State *ICACHE_FLASH_ATTR lua_tothread (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return (!ttisthread(o)) ? NULL : thvalue(o); +} + + +LUA_API const void *ICACHE_FLASH_ATTR lua_topointer (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + switch (ttype(o)) { + case LUA_TTABLE: return hvalue(o); + case LUA_TFUNCTION: return clvalue(o); + case LUA_TTHREAD: return thvalue(o); + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + return lua_touserdata(L, idx); + case LUA_TROTABLE: + case LUA_TLIGHTFUNCTION: + return pvalue(o); + default: return NULL; + } +} + + + +/* +** push functions (C -> stack) +*/ + + +LUA_API void ICACHE_FLASH_ATTR lua_pushnil (lua_State *L) { + lua_lock(L); + setnilvalue(L->top); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_pushnumber (lua_State *L, lua_Number n) { + lua_lock(L); + setnvalue(L->top, n); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_pushinteger (lua_State *L, lua_Integer n) { + lua_lock(L); + setnvalue(L->top, cast_num(n)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_pushlstring (lua_State *L, const char *s, size_t len) { + lua_lock(L); + luaC_checkGC(L); + setsvalue2s(L, L->top, luaS_newlstr(L, s, len)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_pushrolstring (lua_State *L, const char *s, size_t len) { + lua_lock(L); + luaC_checkGC(L); + setsvalue2s(L, L->top, luaS_newrolstr(L, s, len)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_pushstring (lua_State *L, const char *s) { + if (s == NULL) + lua_pushnil(L); + else + lua_pushlstring(L, s, c_strlen(s)); +} + + +LUA_API const char *ICACHE_FLASH_ATTR lua_pushvfstring (lua_State *L, const char *fmt, + va_list argp) { + const char *ret; + lua_lock(L); + luaC_checkGC(L); + ret = luaO_pushvfstring(L, fmt, argp); + lua_unlock(L); + return ret; +} + + +LUA_API const char *ICACHE_FLASH_ATTR lua_pushfstring (lua_State *L, const char *fmt, ...) { + const char *ret; + va_list argp; + lua_lock(L); + luaC_checkGC(L); + va_start(argp, fmt); + ret = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + lua_unlock(L); + return ret; +} + + +LUA_API void ICACHE_FLASH_ATTR lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { + Closure *cl; + lua_lock(L); + luaC_checkGC(L); + api_checknelems(L, n); + cl = luaF_newCclosure(L, n, getcurrenv(L)); + cl->c.f = fn; + L->top -= n; + while (n--) + setobj2n(L, &cl->c.upvalue[n], L->top+n); + setclvalue(L, L->top, cl); + lua_assert(iswhite(obj2gco(cl))); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_pushboolean (lua_State *L, int b) { + lua_lock(L); + setbvalue(L->top, (b != 0)); /* ensure that true is 1 */ + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_pushlightuserdata (lua_State *L, void *p) { + lua_lock(L); + setpvalue(L->top, p); + api_incr_top(L); + lua_unlock(L); +} + +LUA_API void ICACHE_FLASH_ATTR lua_pushrotable (lua_State *L, void *p) { + lua_lock(L); + setrvalue(L->top, p); + api_incr_top(L); + lua_unlock(L); +} + +LUA_API void ICACHE_FLASH_ATTR lua_pushlightfunction(lua_State *L, void *p) { + lua_lock(L); + setfvalue(L->top, p); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API int ICACHE_FLASH_ATTR lua_pushthread (lua_State *L) { + lua_lock(L); + setthvalue(L, L->top, L); + api_incr_top(L); + lua_unlock(L); + return (G(L)->mainthread == L); +} + + + +/* +** get functions (Lua -> stack) +*/ + + +LUA_API void ICACHE_FLASH_ATTR lua_gettable (lua_State *L, int idx) { + StkId t; + lua_lock(L); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + luaV_gettable(L, t, L->top - 1, L->top - 1); + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_getfield (lua_State *L, int idx, const char *k) { + StkId t; + TValue key; + lua_lock(L); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + fixedstack(L); + setsvalue(L, &key, luaS_new(L, k)); + unfixedstack(L); + luaV_gettable(L, t, &key, L->top); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_rawget (lua_State *L, int idx) { + StkId t; + const TValue *res; + lua_lock(L); + t = index2adr(L, idx); + api_check(L, ttistable(t) || ttisrotable(t)); + res = ttistable(t) ? luaH_get(hvalue(t), L->top - 1) : luaH_get_ro(rvalue(t), L->top - 1); + setobj2s(L, L->top - 1, res); + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_rawgeti (lua_State *L, int idx, int n) { + StkId o; + lua_lock(L); + o = index2adr(L, idx); + api_check(L, ttistable(o) || ttisrotable(o)); + setobj2s(L, L->top, ttistable(o) ? luaH_getnum(hvalue(o), n) : luaH_getnum_ro(rvalue(o), n)) + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_createtable (lua_State *L, int narray, int nrec) { + lua_lock(L); + luaC_checkGC(L); + sethvalue(L, L->top, luaH_new(L, narray, nrec)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API int ICACHE_FLASH_ATTR lua_getmetatable (lua_State *L, int objindex) { + const TValue *obj; + Table *mt = NULL; + int res; + lua_lock(L); + obj = index2adr(L, objindex); + switch (ttype(obj)) { + case LUA_TTABLE: + mt = hvalue(obj)->metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(obj)->metatable; + break; + case LUA_TROTABLE: + mt = (Table*)luaR_getmeta(rvalue(obj)); + break; + default: + mt = G(L)->mt[ttype(obj)]; + break; + } + if (mt == NULL) + res = 0; + else { + if(luaR_isrotable(mt)) + setrvalue(L->top, mt) + else + sethvalue(L, L->top, mt) + api_incr_top(L); + res = 1; + } + lua_unlock(L); + return res; +} + + +LUA_API void ICACHE_FLASH_ATTR lua_getfenv (lua_State *L, int idx) { + StkId o; + lua_lock(L); + o = index2adr(L, idx); + api_checkvalidindex(L, o); + switch (ttype(o)) { + case LUA_TFUNCTION: + sethvalue(L, L->top, clvalue(o)->c.env); + break; + case LUA_TUSERDATA: + sethvalue(L, L->top, uvalue(o)->env); + break; + case LUA_TTHREAD: + setobj2s(L, L->top, gt(thvalue(o))); + break; + default: + setnilvalue(L->top); + break; + } + api_incr_top(L); + lua_unlock(L); +} + + +/* +** set functions (stack -> Lua) +*/ + + +LUA_API void ICACHE_FLASH_ATTR lua_settable (lua_State *L, int idx) { + StkId t; + lua_lock(L); + api_checknelems(L, 2); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + luaV_settable(L, t, L->top - 2, L->top - 1); + L->top -= 2; /* pop index and value */ + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_setfield (lua_State *L, int idx, const char *k) { + StkId t; + lua_lock(L); + api_checknelems(L, 1); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + setsvalue2s(L, L->top, luaS_new(L, k)); + api_incr_top(L); + luaV_settable(L, t, L->top - 1, L->top - 2); + L->top -= 2; /* pop key and value */ + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_rawset (lua_State *L, int idx) { + StkId t; + lua_lock(L); + api_checknelems(L, 2); + t = index2adr(L, idx); + api_check(L, ttistable(t)); + fixedstack(L); + setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1); + unfixedstack(L); + luaC_barriert(L, hvalue(t), L->top-1); + L->top -= 2; + lua_unlock(L); +} + + +LUA_API void ICACHE_FLASH_ATTR lua_rawseti (lua_State *L, int idx, int n) { + StkId o; + lua_lock(L); + api_checknelems(L, 1); + o = index2adr(L, idx); + api_check(L, ttistable(o)); + fixedstack(L); + setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1); + unfixedstack(L); + luaC_barriert(L, hvalue(o), L->top-1); + L->top--; + lua_unlock(L); +} + + +LUA_API int ICACHE_FLASH_ATTR lua_setmetatable (lua_State *L, int objindex) { + TValue *obj; + Table *mt; + int isrometa = 0; + lua_lock(L); + api_checknelems(L, 1); + obj = index2adr(L, objindex); + api_checkvalidindex(L, obj); + if (ttisnil(L->top - 1)) + mt = NULL; + else { + api_check(L, ttistable(L->top - 1) || ttisrotable(L->top - 1)); + if (ttistable(L->top - 1)) + mt = hvalue(L->top - 1); + else { + mt = (Table*)rvalue(L->top - 1); + isrometa = 1; + } + } + switch (ttype(obj)) { + case LUA_TTABLE: { + hvalue(obj)->metatable = mt; + if (mt && !isrometa) + luaC_objbarriert(L, hvalue(obj), mt); + break; + } + case LUA_TUSERDATA: { + uvalue(obj)->metatable = mt; + if (mt && !isrometa) + luaC_objbarrier(L, rawuvalue(obj), mt); + break; + } + default: { + G(L)->mt[ttype(obj)] = mt; + break; + } + } + L->top--; + lua_unlock(L); + return 1; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_setfenv (lua_State *L, int idx) { + StkId o; + int res = 1; + lua_lock(L); + api_checknelems(L, 1); + o = index2adr(L, idx); + api_checkvalidindex(L, o); + api_check(L, ttistable(L->top - 1)); + switch (ttype(o)) { + case LUA_TFUNCTION: + clvalue(o)->c.env = hvalue(L->top - 1); + break; + case LUA_TUSERDATA: + uvalue(o)->env = hvalue(L->top - 1); + break; + case LUA_TTHREAD: + sethvalue(L, gt(thvalue(o)), hvalue(L->top - 1)); + break; + default: + res = 0; + break; + } + if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1)); + L->top--; + lua_unlock(L); + return res; +} + + +/* +** `load' and `call' functions (run Lua code) +*/ + + +#define adjustresults(L,nres) \ + { if (nres == LUA_MULTRET && L->top >= L->ci->top) L->ci->top = L->top; } + + +#define checkresults(L,na,nr) \ + api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na))) + + +LUA_API void ICACHE_FLASH_ATTR lua_call (lua_State *L, int nargs, int nresults) { + StkId func; + lua_lock(L); + api_checknelems(L, nargs+1); + checkresults(L, nargs, nresults); + func = L->top - (nargs+1); + luaD_call(L, func, nresults); + adjustresults(L, nresults); + lua_unlock(L); +} + + + +/* +** Execute a protected call. +*/ +struct CallS { /* data to `f_call' */ + StkId func; + int nresults; +}; + + +static void ICACHE_FLASH_ATTR f_call (lua_State *L, void *ud) { + struct CallS *c = cast(struct CallS *, ud); + luaD_call(L, c->func, c->nresults); +} + + + +LUA_API int ICACHE_FLASH_ATTR lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) { + struct CallS c; + int status; + ptrdiff_t func; + lua_lock(L); + api_checknelems(L, nargs+1); + checkresults(L, nargs, nresults); + if (errfunc == 0) + func = 0; + else { + StkId o = index2adr(L, errfunc); + api_checkvalidindex(L, o); + func = savestack(L, o); + } + c.func = L->top - (nargs+1); /* function to be called */ + c.nresults = nresults; + status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); + adjustresults(L, nresults); + lua_unlock(L); + return status; +} + + +/* +** Execute a protected C call. +*/ +struct CCallS { /* data to `f_Ccall' */ + lua_CFunction func; + void *ud; +}; + + +static void ICACHE_FLASH_ATTR f_Ccall (lua_State *L, void *ud) { + struct CCallS *c = cast(struct CCallS *, ud); + Closure *cl; + cl = luaF_newCclosure(L, 0, getcurrenv(L)); + cl->c.f = c->func; + setclvalue(L, L->top, cl); /* push function */ + api_incr_top(L); + setpvalue(L->top, c->ud); /* push only argument */ + api_incr_top(L); + luaD_call(L, L->top - 2, 0); +} + + +LUA_API int ICACHE_FLASH_ATTR lua_cpcall (lua_State *L, lua_CFunction func, void *ud) { + struct CCallS c; + int status; + lua_lock(L); + c.func = func; + c.ud = ud; + status = luaD_pcall(L, f_Ccall, &c, savestack(L, L->top), 0); + lua_unlock(L); + return status; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_load (lua_State *L, lua_Reader reader, void *data, + const char *chunkname) { + ZIO z; + int status; + lua_lock(L); + if (!chunkname) chunkname = "?"; + luaZ_init(L, &z, reader, data); + status = luaD_protectedparser(L, &z, chunkname); + lua_unlock(L); + return status; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_dump (lua_State *L, lua_Writer writer, void *data) { + int status; + TValue *o; + lua_lock(L); + api_checknelems(L, 1); + o = L->top - 1; + if (isLfunction(o)) + status = luaU_dump(L, clvalue(o)->l.p, writer, data, 0); + else + status = 1; + lua_unlock(L); + return status; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_status (lua_State *L) { + return L->status; +} + + +/* +** Garbage-collection function +*/ + +LUA_API int ICACHE_FLASH_ATTR lua_gc (lua_State *L, int what, int data) { + int res = 0; + global_State *g; + lua_lock(L); + g = G(L); + switch (what) { + case LUA_GCSTOP: { + set_block_gc(L); + break; + } + case LUA_GCRESTART: { + unset_block_gc(L); + break; + } + case LUA_GCCOLLECT: { + luaC_fullgc(L); + break; + } + case LUA_GCCOUNT: { + /* GC values are expressed in Kbytes: #bytes/2^10 */ + res = cast_int(g->totalbytes >> 10); + break; + } + case LUA_GCCOUNTB: { + res = cast_int(g->totalbytes & 0x3ff); + break; + } + case LUA_GCSTEP: { + if(is_block_gc(L)) { + res = 1; /* gc is block so we need to pretend that the collection cycle finished. */ + break; + } + lu_mem a = (cast(lu_mem, data) << 10); + if (a <= g->totalbytes) + g->GCthreshold = g->totalbytes - a; + else + g->GCthreshold = 0; + while (g->GCthreshold <= g->totalbytes) { + luaC_step(L); + if (g->gcstate == GCSpause) { /* end of cycle? */ + res = 1; /* signal it */ + break; + } + } + break; + } + case LUA_GCSETPAUSE: { + res = g->gcpause; + g->gcpause = data; + break; + } + case LUA_GCSETSTEPMUL: { + res = g->gcstepmul; + g->gcstepmul = data; + break; + } + case LUA_GCSETMEMLIMIT: { + /* GC values are expressed in Kbytes: #bytes/2^10 */ + lu_mem new_memlimit = (cast(lu_mem, data) << 10); + if(new_memlimit > 0 && new_memlimit < g->totalbytes) { + /* run a full GC to make totalbytes < the new limit. */ + luaC_fullgc(L); + if(new_memlimit < g->totalbytes) + new_memlimit = (g->totalbytes + 1024) & ~(1024-1); /* round up to next multiple of 1024 */ + } + g->memlimit = new_memlimit; + /* new memlimit might be > then requested memlimit. */ + res = cast_int(new_memlimit >> 10); + break; + } + case LUA_GCGETMEMLIMIT: { + res = cast_int(g->memlimit >> 10); + break; + } + default: res = -1; /* invalid option */ + } + lua_unlock(L); + return res; +} + + + +/* +** miscellaneous functions +*/ + + +LUA_API int ICACHE_FLASH_ATTR lua_error (lua_State *L) { + lua_lock(L); + api_checknelems(L, 1); + luaG_errormsg(L); + lua_unlock(L); + return 0; /* to avoid warnings */ +} + + +LUA_API int ICACHE_FLASH_ATTR lua_next (lua_State *L, int idx) { + StkId t; + int more; + lua_lock(L); + t = index2adr(L, idx); + api_check(L, ttistable(t) || ttisrotable(t)); + more = ttistable(t) ? luaH_next(L, hvalue(t), L->top - 1) : luaH_next_ro(L, rvalue(t), L->top - 1); + if (more) { + api_incr_top(L); + } + else /* no more elements */ + L->top -= 1; /* remove key */ + lua_unlock(L); + return more; +} + + +LUA_API void ICACHE_FLASH_ATTR lua_concat (lua_State *L, int n) { + lua_lock(L); + api_checknelems(L, n); + if (n >= 2) { + luaC_checkGC(L); + luaV_concat(L, n, cast_int(L->top - L->base) - 1); + L->top -= (n-1); + } + else if (n == 0) { /* push empty string */ + setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); + api_incr_top(L); + } + /* else n == 1; nothing to do */ + lua_unlock(L); +} + + +LUA_API lua_Alloc ICACHE_FLASH_ATTR lua_getallocf (lua_State *L, void **ud) { + lua_Alloc f; + lua_lock(L); + if (ud) *ud = G(L)->ud; + f = G(L)->frealloc; + lua_unlock(L); + return f; +} + + +LUA_API void ICACHE_FLASH_ATTR lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { + lua_lock(L); + G(L)->ud = ud; + G(L)->frealloc = f; + lua_unlock(L); +} + + +LUA_API void *ICACHE_FLASH_ATTR lua_newuserdata (lua_State *L, size_t size) { + Udata *u; + lua_lock(L); + luaC_checkGC(L); + u = luaS_newudata(L, size, getcurrenv(L)); + setuvalue(L, L->top, u); + api_incr_top(L); + lua_unlock(L); + return u + 1; +} + + + + +static const char *ICACHE_FLASH_ATTR aux_upvalue (StkId fi, int n, TValue **val) { + Closure *f; + if (!ttisfunction(fi)) return NULL; + f = clvalue(fi); + if (f->c.isC) { + if (!(1 <= n && n <= f->c.nupvalues)) return NULL; + *val = &f->c.upvalue[n-1]; + return ""; + } + else { + Proto *p = f->l.p; + if (!(1 <= n && n <= p->sizeupvalues)) return NULL; + *val = f->l.upvals[n-1]->v; + return getstr(p->upvalues[n-1]); + } +} + + +LUA_API const char *ICACHE_FLASH_ATTR lua_getupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val; + lua_lock(L); + name = aux_upvalue(index2adr(L, funcindex), n, &val); + if (name) { + setobj2s(L, L->top, val); + api_incr_top(L); + } + lua_unlock(L); + return name; +} + + +LUA_API const char *ICACHE_FLASH_ATTR lua_setupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val; + StkId fi; + lua_lock(L); + fi = index2adr(L, funcindex); + api_checknelems(L, 1); + name = aux_upvalue(fi, n, &val); + if (name) { + L->top--; + setobj(L, val, L->top); + luaC_barrier(L, clvalue(fi), L->top); + } + lua_unlock(L); + return name; +} + diff --git a/app/lua/lapi.h b/app/lua/lapi.h new file mode 100644 index 00000000..2c3fab24 --- /dev/null +++ b/app/lua/lapi.h @@ -0,0 +1,16 @@ +/* +** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions from Lua API +** See Copyright Notice in lua.h +*/ + +#ifndef lapi_h +#define lapi_h + + +#include "lobject.h" + + +LUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o); + +#endif diff --git a/app/lua/lauxlib.c b/app/lua/lauxlib.c new file mode 100644 index 00000000..a7b6f6a6 --- /dev/null +++ b/app/lua/lauxlib.c @@ -0,0 +1,819 @@ +/* +** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#include "c_ctype.h" +// #include "c_errno.h" +#include "c_stdarg.h" +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_string.h" +#include "flash_fs.h" + +/* This file uses only the official API of Lua. +** Any function declared here could be written as an application function. +*/ + +#define lauxlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lrotable.h" + +#include "lauxlib.h" +#include "lgc.h" +#include "ldo.h" +#include "lobject.h" +#include "lstate.h" +#include "legc.h" + +#define FREELIST_REF 0 /* free list of references */ + + +/* convert a stack index to positive */ +#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \ + lua_gettop(L) + (i) + 1) + +// Parameters for luaI_openlib +#define LUA_USECCLOSURES 0 +#define LUA_USELIGHTFUNCTIONS 1 + +/* +** {====================================================== +** Error-report functions +** ======================================================= +*/ + + +LUALIB_API int ICACHE_FLASH_ATTR luaL_argerror (lua_State *L, int narg, const char *extramsg) { + lua_Debug ar; + if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ + return luaL_error(L, "bad argument #%d (%s)", narg, extramsg); + lua_getinfo(L, "n", &ar); + if (c_strcmp(ar.namewhat, "method") == 0) { + narg--; /* do not count `self' */ + if (narg == 0) /* error is in the self argument itself? */ + return luaL_error(L, "calling " LUA_QS " on bad self (%s)", + ar.name, extramsg); + } + if (ar.name == NULL) + ar.name = "?"; + return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)", + narg, ar.name, extramsg); +} + + +LUALIB_API int ICACHE_FLASH_ATTR luaL_typerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", + tname, luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); +} + + +static void ICACHE_FLASH_ATTR tag_error (lua_State *L, int narg, int tag) { + luaL_typerror(L, narg, lua_typename(L, tag)); +} + + +LUALIB_API void ICACHE_FLASH_ATTR luaL_where (lua_State *L, int level) { + lua_Debug ar; + if (lua_getstack(L, level, &ar)) { /* check function at level */ + lua_getinfo(L, "Sl", &ar); /* get info about it */ + if (ar.currentline > 0) { /* is there info? */ + lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); + return; + } + } + lua_pushliteral(L, ""); /* else, no information available... */ +} + + +LUALIB_API int ICACHE_FLASH_ATTR luaL_error (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + luaL_where(L, 1); + lua_pushvfstring(L, fmt, argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + +/* }====================================================== */ + + +LUALIB_API int ICACHE_FLASH_ATTR luaL_checkoption (lua_State *L, int narg, const char *def, + const char *const lst[]) { + const char *name = (def) ? luaL_optstring(L, narg, def) : + luaL_checkstring(L, narg); + int i; + for (i=0; lst[i]; i++) + if (c_strcmp(lst[i], name) == 0) + return i; + return luaL_argerror(L, narg, + lua_pushfstring(L, "invalid option " LUA_QS, name)); +} + + +LUALIB_API int ICACHE_FLASH_ATTR luaL_newmetatable (lua_State *L, const char *tname) { + lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */ + if (!lua_isnil(L, -1)) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + lua_newtable(L); /* create metatable */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ + return 1; +} + +LUALIB_API int ICACHE_FLASH_ATTR luaL_rometatable (lua_State *L, const char* tname, void *p) { + lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */ + if (!lua_isnil(L, -1)) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + lua_pushrotable(L, p); + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ + return 1; +} + +LUALIB_API void *ICACHE_FLASH_ATTR luaL_checkudata (lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */ + if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + } + luaL_typerror(L, ud, tname); /* else error */ + return NULL; /* to avoid warnings */ +} + + +LUALIB_API void ICACHE_FLASH_ATTR luaL_checkstack (lua_State *L, int space, const char *mes) { + if (!lua_checkstack(L, space)) + luaL_error(L, "stack overflow (%s)", mes); +} + + +LUALIB_API void ICACHE_FLASH_ATTR luaL_checktype (lua_State *L, int narg, int t) { + if (lua_type(L, narg) != t) + tag_error(L, narg, t); +} + +LUALIB_API void ICACHE_FLASH_ATTR luaL_checkanyfunction (lua_State *L, int narg) { + if (lua_type(L, narg) != LUA_TFUNCTION && lua_type(L, narg) != LUA_TLIGHTFUNCTION) { + const char *msg = lua_pushfstring(L, "function or lightfunction expected, got %s", + luaL_typename(L, narg)); + luaL_argerror(L, narg, msg); + } +} + +LUALIB_API void ICACHE_FLASH_ATTR luaL_checkanytable (lua_State *L, int narg) { + if (lua_type(L, narg) != LUA_TTABLE && lua_type(L, narg) != LUA_TROTABLE) { + const char *msg = lua_pushfstring(L, "table or rotable expected, got %s", + luaL_typename(L, narg)); + luaL_argerror(L, narg, msg); + } +} + + +LUALIB_API void ICACHE_FLASH_ATTR luaL_checkany (lua_State *L, int narg) { + if (lua_type(L, narg) == LUA_TNONE) + luaL_argerror(L, narg, "value expected"); +} + + +LUALIB_API const char *ICACHE_FLASH_ATTR luaL_checklstring (lua_State *L, int narg, size_t *len) { + const char *s = lua_tolstring(L, narg, len); + if (!s) tag_error(L, narg, LUA_TSTRING); + return s; +} + + +LUALIB_API const char *ICACHE_FLASH_ATTR luaL_optlstring (lua_State *L, int narg, + const char *def, size_t *len) { + if (lua_isnoneornil(L, narg)) { + if (len) + *len = (def ? c_strlen(def) : 0); + return def; + } + else return luaL_checklstring(L, narg, len); +} + + +LUALIB_API lua_Number ICACHE_FLASH_ATTR luaL_checknumber (lua_State *L, int narg) { + lua_Number d = lua_tonumber(L, narg); + if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Number ICACHE_FLASH_ATTR luaL_optnumber (lua_State *L, int narg, lua_Number def) { + return luaL_opt(L, luaL_checknumber, narg, def); +} + + +LUALIB_API lua_Integer ICACHE_FLASH_ATTR luaL_checkinteger (lua_State *L, int narg) { + lua_Integer d = lua_tointeger(L, narg); + if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Integer ICACHE_FLASH_ATTR luaL_optinteger (lua_State *L, int narg, + lua_Integer def) { + return luaL_opt(L, luaL_checkinteger, narg, def); +} + + +LUALIB_API int ICACHE_FLASH_ATTR luaL_getmetafield (lua_State *L, int obj, const char *event) { + if (!lua_getmetatable(L, obj)) /* no metatable? */ + return 0; + lua_pushstring(L, event); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); /* remove metatable and metafield */ + return 0; + } + else { + lua_remove(L, -2); /* remove only metatable */ + return 1; + } +} + + +LUALIB_API int ICACHE_FLASH_ATTR luaL_callmeta (lua_State *L, int obj, const char *event) { + obj = abs_index(L, obj); + if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ + return 0; + lua_pushvalue(L, obj); + lua_call(L, 1, 1); + return 1; +} + + +LUALIB_API void ICACHE_FLASH_ATTR (luaL_register) (lua_State *L, const char *libname, + const luaL_Reg *l) { + luaI_openlib(L, libname, l, 0, LUA_USECCLOSURES); +} + +LUALIB_API void ICACHE_FLASH_ATTR (luaL_register_light) (lua_State *L, const char *libname, + const luaL_Reg *l) { +#if LUA_OPTIMIZE_MEMORY > 0 + luaI_openlib(L, libname, l, 0, LUA_USELIGHTFUNCTIONS); +#else + luaI_openlib(L, libname, l, 0, LUA_USECCLOSURES); +#endif +} + +static int ICACHE_FLASH_ATTR libsize (const luaL_Reg *l) { + int size = 0; + for (; l->name; l++) size++; + return size; +} + + +LUALIB_API void ICACHE_FLASH_ATTR luaI_openlib (lua_State *L, const char *libname, + const luaL_Reg *l, int nup, int ftype) { + if (libname) { + int size = libsize(l); + /* check whether lib already exists */ + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); + lua_getfield(L, -1, libname); /* get _LOADED[libname] */ + if (!lua_istable(L, -1)) { /* not found? */ + lua_pop(L, 1); /* remove previous result */ + /* try global variable (and create one if it does not exist) */ + if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL) + luaL_error(L, "name conflict for module " LUA_QS, libname); + lua_pushvalue(L, -1); + lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */ + } + lua_remove(L, -2); /* remove _LOADED table */ + lua_insert(L, -(nup+1)); /* move library table to below upvalues */ + } + for (; l->name; l++) { + int i; + for (i=0; ifunc); + else + lua_pushcclosure(L, l->func, nup); + lua_setfield(L, -(nup+2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + + + +/* +** {====================================================== +** getn-setn: size for arrays +** ======================================================= +*/ + +#if defined(LUA_COMPAT_GETN) + +static int ICACHE_FLASH_ATTR checkint (lua_State *L, int topop) { + int n = (lua_type(L, -1) == LUA_TNUMBER) ? lua_tointeger(L, -1) : -1; + lua_pop(L, topop); + return n; +} + + +static void ICACHE_FLASH_ATTR getsizes (lua_State *L) { + lua_getfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); + if (lua_isnil(L, -1)) { /* no `size' table? */ + lua_pop(L, 1); /* remove nil */ + lua_newtable(L); /* create it */ + lua_pushvalue(L, -1); /* `size' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "kv"); + lua_setfield(L, -2, "__mode"); /* metatable(N).__mode = "kv" */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); /* store in register */ + } +} + + +LUALIB_API void ICACHE_FLASH_ATTR luaL_setn (lua_State *L, int t, int n) { + t = abs_index(L, t); + lua_pushliteral(L, "n"); + lua_rawget(L, t); + if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */ + lua_pushliteral(L, "n"); /* use it */ + lua_pushinteger(L, n); + lua_rawset(L, t); + } + else { /* use `sizes' */ + getsizes(L); + lua_pushvalue(L, t); + lua_pushinteger(L, n); + lua_rawset(L, -3); /* sizes[t] = n */ + lua_pop(L, 1); /* remove `sizes' */ + } +} + + +LUALIB_API int ICACHE_FLASH_ATTR luaL_getn (lua_State *L, int t) { + int n; + t = abs_index(L, t); + lua_pushliteral(L, "n"); /* try t.n */ + lua_rawget(L, t); + if ((n = checkint(L, 1)) >= 0) return n; + getsizes(L); /* else try sizes[t] */ + lua_pushvalue(L, t); + lua_rawget(L, -2); + if ((n = checkint(L, 2)) >= 0) return n; + return (int)lua_objlen(L, t); +} + +#endif + +/* }====================================================== */ + + + +LUALIB_API const char *ICACHE_FLASH_ATTR luaL_gsub (lua_State *L, const char *s, const char *p, + const char *r) { + const char *wild; + size_t l = c_strlen(p); + luaL_Buffer b; + luaL_buffinit(L, &b); + while ((wild = c_strstr(s, p)) != NULL) { + luaL_addlstring(&b, s, wild - s); /* push prefix */ + luaL_addstring(&b, r); /* push replacement in place of pattern */ + s = wild + l; /* continue after `p' */ + } + luaL_addstring(&b, s); /* push last suffix */ + luaL_pushresult(&b); + return lua_tostring(L, -1); +} + + +LUALIB_API const char *ICACHE_FLASH_ATTR luaL_findtable (lua_State *L, int idx, + const char *fname, int szhint) { + const char *e; + lua_pushvalue(L, idx); + do { + e = c_strchr(fname, '.'); + if (e == NULL) e = fname + c_strlen(fname); + lua_pushlstring(L, fname, e - fname); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + /* If looking for a global variable, check the rotables too */ + void *ptable = luaR_findglobal(fname, e - fname); + if (ptable) { + lua_pop(L, 1); + lua_pushrotable(L, ptable); + } + } + if (lua_isnil(L, -1)) { /* no such field? */ + lua_pop(L, 1); /* remove this nil */ + lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ + lua_pushlstring(L, fname, e - fname); + lua_pushvalue(L, -2); + lua_settable(L, -4); /* set new table into field */ + } + else if (!lua_istable(L, -1) && !lua_isrotable(L, -1)) { /* field has a non-table value? */ + lua_pop(L, 2); /* remove table and value */ + return fname; /* return problematic part of the name */ + } + lua_remove(L, -2); /* remove previous table */ + fname = e + 1; + } while (*e == '.'); + return NULL; +} + + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + +#define bufflen(B) ((B)->p - (B)->buffer) +#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) + +#define LIMIT (LUA_MINSTACK/2) + + +static int ICACHE_FLASH_ATTR emptybuffer (luaL_Buffer *B) { + size_t l = bufflen(B); + if (l == 0) return 0; /* put nothing on stack */ + else { + lua_pushlstring(B->L, B->buffer, l); + B->p = B->buffer; + B->lvl++; + return 1; + } +} + + +static void ICACHE_FLASH_ATTR adjuststack (luaL_Buffer *B) { + if (B->lvl > 1) { + lua_State *L = B->L; + int toget = 1; /* number of levels to concat */ + size_t toplen = lua_strlen(L, -1); + do { + size_t l = lua_strlen(L, -(toget+1)); + if (B->lvl - toget + 1 >= LIMIT || toplen > l) { + toplen += l; + toget++; + } + else break; + } while (toget < B->lvl); + lua_concat(L, toget); + B->lvl = B->lvl - toget + 1; + } +} + + +LUALIB_API char *ICACHE_FLASH_ATTR luaL_prepbuffer (luaL_Buffer *B) { + if (emptybuffer(B)) + adjuststack(B); + return B->buffer; +} + + +LUALIB_API void ICACHE_FLASH_ATTR luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { + while (l--) + luaL_addchar(B, *s++); +} + + +LUALIB_API void ICACHE_FLASH_ATTR luaL_addstring (luaL_Buffer *B, const char *s) { + luaL_addlstring(B, s, c_strlen(s)); +} + + +LUALIB_API void ICACHE_FLASH_ATTR luaL_pushresult (luaL_Buffer *B) { + emptybuffer(B); + lua_concat(B->L, B->lvl); + B->lvl = 1; +} + + +LUALIB_API void ICACHE_FLASH_ATTR luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t vl; + const char *s = lua_tolstring(L, -1, &vl); + if (vl <= bufffree(B)) { /* fit into buffer? */ + c_memcpy(B->p, s, vl); /* put it there */ + B->p += vl; + lua_pop(L, 1); /* remove from stack */ + } + else { + if (emptybuffer(B)) + lua_insert(L, -2); /* put buffer before new value */ + B->lvl++; /* add new value into B stack */ + adjuststack(B); + } +} + + +LUALIB_API void ICACHE_FLASH_ATTR luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; + B->p = B->buffer; + B->lvl = 0; +} + +/* }====================================================== */ + + +LUALIB_API int ICACHE_FLASH_ATTR luaL_ref (lua_State *L, int t) { + int ref; + t = abs_index(L, t); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove from stack */ + return LUA_REFNIL; /* `nil' has a unique fixed reference */ + } + lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ + ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */ + lua_pop(L, 1); /* remove it from stack */ + if (ref != 0) { /* any free element? */ + lua_rawgeti(L, t, ref); /* remove it from list */ + lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ + } + else { /* no free elements */ + ref = (int)lua_objlen(L, t); + ref++; /* create new reference */ + } + lua_rawseti(L, t, ref); + return ref; +} + + +LUALIB_API void ICACHE_FLASH_ATTR luaL_unref (lua_State *L, int t, int ref) { + if (ref >= 0) { + t = abs_index(L, t); + lua_rawgeti(L, t, FREELIST_REF); + lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ + lua_pushinteger(L, ref); + lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ + } +} + + + +/* +** {====================================================== +** Load functions +** ======================================================= +*/ + +#if 0 + +typedef struct LoadF { + int extraline; + FILE *f; + char buff[LUAL_BUFFERSIZE]; +} LoadF; + + +static const char *ICACHE_FLASH_ATTR getF (lua_State *L, void *ud, size_t *size) { + LoadF *lf = (LoadF *)ud; + (void)L; + if (lf->extraline) { + lf->extraline = 0; + *size = 1; + return "\n"; + } + if (c_feof(lf->f)) return NULL; + *size = c_fread(lf->buff, 1, sizeof(lf->buff), lf->f); + return (*size > 0) ? lf->buff : NULL; +} + + +static int ICACHE_FLASH_ATTR errfile (lua_State *L, const char *what, int fnameindex) { + const char *serr = c_strerror(errno); + const char *filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + + +LUALIB_API int ICACHE_FLASH_ATTR luaL_loadfile (lua_State *L, const char *filename) { + LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + lf.extraline = 0; + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = c_stdin; + } + else { + lua_pushfstring(L, "@%s", filename); + lf.f = c_fopen(filename, "r"); + if (lf.f == NULL) return errfile(L, "open", fnameindex); + } + c = c_getc(lf.f); + if (c == '#') { /* Unix exec. file? */ + lf.extraline = 1; + while ((c = c_getc(lf.f)) != EOF && c != '\n') ; /* skip first line */ + if (c == '\n') c = c_getc(lf.f); + } + if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ + lf.f = c_freopen(filename, "rb", lf.f); /* reopen in binary mode */ + if (lf.f == NULL) return errfile(L, "reopen", fnameindex); + /* skip eventual `#!...' */ + while ((c = c_getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ; + lf.extraline = 0; + } + c_ungetc(c, lf.f); + status = lua_load(L, getF, &lf, lua_tostring(L, -1)); + readstatus = c_ferror(lf.f); + if (filename) c_fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from `lua_load' */ + return errfile(L, "read", fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + +#else + +#include "c_fcntl.h" + +typedef struct LoadFSF { + int extraline; + int f; + char buff[LUAL_BUFFERSIZE]; +} LoadFSF; + + +static const char *ICACHE_FLASH_ATTR getFSF (lua_State *L, void *ud, size_t *size) { + LoadFSF *lf = (LoadFSF *)ud; + (void)L; + if (lf->extraline) { + lf->extraline = 0; + *size = 1; + return "\n"; + } + if (fs_eof(lf->f)) return NULL; + *size = fs_read(lf->f, lf->buff, sizeof(lf->buff)); + return (*size > 0) ? lf->buff : NULL; +} + + +static int ICACHE_FLASH_ATTR errfsfile (lua_State *L, const char *what, int fnameindex) { + const char *filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot %s %s", what, filename); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + + +LUALIB_API int ICACHE_FLASH_ATTR luaL_loadfsfile (lua_State *L, const char *filename) { + LoadFSF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + lf.extraline = 0; + if (filename == NULL) { + return luaL_error(L, "filename is NULL"); + } + else { + lua_pushfstring(L, "@%s", filename); + lf.f = fs_open(filename, FS_RDONLY); + if (lf.f < FS_OPEN_OK) return errfsfile(L, "open", fnameindex); + } + c = fs_getc(lf.f); + if (c == '#') { /* Unix exec. file? */ + lf.extraline = 1; + while ((c = fs_getc(lf.f)) != EOF && c != '\n') ; /* skip first line */ + if (c == '\n') c = fs_getc(lf.f); + } + if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ + fs_close(lf.f); + lf.f = fs_open(filename, FS_RDONLY); /* reopen in binary mode */ + if (lf.f < FS_OPEN_OK) return errfsfile(L, "reopen", fnameindex); + /* skip eventual `#!...' */ + while ((c = fs_getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ; + lf.extraline = 0; + } + fs_ungetc(c, lf.f); + status = lua_load(L, getFSF, &lf, lua_tostring(L, -1)); + + if (filename) fs_close(lf.f); /* close file (even in case of errors) */ + lua_remove(L, fnameindex); + return status; +} + +#endif + +typedef struct LoadS { + const char *s; + size_t size; +} LoadS; + + +static const char *ICACHE_FLASH_ATTR getS (lua_State *L, void *ud, size_t *size) { + LoadS *ls = (LoadS *)ud; + (void)L; + if (L == NULL && size == NULL) // direct mode check + return NULL; + if (ls->size == 0) return NULL; + *size = ls->size; + ls->size = 0; + return ls->s; +} + + +LUALIB_API int ICACHE_FLASH_ATTR luaL_loadbuffer (lua_State *L, const char *buff, size_t size, + const char *name) { + LoadS ls; + ls.s = buff; + ls.size = size; + return lua_load(L, getS, &ls, name); +} + + +LUALIB_API int ICACHE_FLASH_ATTR (luaL_loadstring) (lua_State *L, const char *s) { + return luaL_loadbuffer(L, s, c_strlen(s), s); +} + + + +/* }====================================================== */ + + +static int ICACHE_FLASH_ATTR l_check_memlimit(lua_State *L, size_t needbytes) { + global_State *g = G(L); + int cycle_count = 0; + lu_mem limit = g->memlimit - needbytes; + /* don't allow allocation if it requires more memory then the total limit. */ + if (needbytes > g->memlimit) return 1; + /* make sure the GC is not disabled. */ + if (!is_block_gc(L)) { + while (g->totalbytes >= limit) { + /* only allow the GC to finished atleast 1 full cycle. */ + if (g->gcstate == GCSpause && ++cycle_count > 1) break; + luaC_step(L); + } + } + return (g->totalbytes >= limit) ? 1 : 0; +} + + +static void *ICACHE_FLASH_ATTR l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { + lua_State *L = (lua_State *)ud; + int mode = L == NULL ? 0 : G(L)->egcmode; + void *nptr; + + if (nsize == 0) { + c_free(ptr); + return NULL; + } + if (L != NULL && (mode & EGC_ALWAYS)) /* always collect memory if requested */ + luaC_fullgc(L); + if(nsize > osize && L != NULL) { +#if defined(LUA_STRESS_EMERGENCY_GC) + luaC_fullgc(L); +#endif + if(G(L)->memlimit > 0 && (mode & EGC_ON_MEM_LIMIT) && l_check_memlimit(L, nsize - osize)) + return NULL; + } + nptr = (void *)c_realloc(ptr, nsize); + if (nptr == NULL && L != NULL && (mode & EGC_ON_ALLOC_FAILURE)) { + luaC_fullgc(L); /* emergency full collection. */ + nptr = (void *)c_realloc(ptr, nsize); /* try allocation again */ + } + return nptr; +} + + +static int ICACHE_FLASH_ATTR panic (lua_State *L) { + (void)L; /* to avoid warnings */ +#if defined(LUA_USE_STDIO) + c_fprintf(c_stderr, "PANIC: unprotected error in call to Lua API (%s)\n", + lua_tostring(L, -1)); +#else + luai_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", + lua_tostring(L, -1)); +#endif + return 0; +} + + +LUALIB_API lua_State *ICACHE_FLASH_ATTR luaL_newstate (void) { + lua_State *L = lua_newstate(l_alloc, NULL); + lua_setallocf(L, l_alloc, L); /* allocator need lua_State. */ + if (L) lua_atpanic(L, &panic); + return L; +} + diff --git a/app/lua/lauxlib.h b/app/lua/lauxlib.h new file mode 100644 index 00000000..fbaaf1a6 --- /dev/null +++ b/app/lua/lauxlib.h @@ -0,0 +1,188 @@ +/* +** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +//#include "c_stddef.h" +#include "c_stdio.h" + +#include "lua.h" + + +#if defined(LUA_COMPAT_GETN) +LUALIB_API int (luaL_getn) (lua_State *L, int t); +LUALIB_API void (luaL_setn) (lua_State *L, int t, int n); +#else +#define luaL_getn(L,i) ((int)lua_objlen(L, i)) +#define luaL_setn(L,i,j) ((void)0) /* no op! */ +#endif + +#if defined(LUA_COMPAT_OPENLIB) +#define luaI_openlib luaL_openlib +#endif + + +/* extra error code for `luaL_load' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + + + +LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname, + const luaL_Reg *l, int nup, int ftype); +LUALIB_API void (luaL_register) (lua_State *L, const char *libname, + const luaL_Reg *l); +LUALIB_API void (luaL_register_light) (lua_State *L, const char *libname, + const luaL_Reg *l); +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname); +LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int narg); +LUALIB_API void (luaL_checkanyfunction) (lua_State *L, int narg); +LUALIB_API void (luaL_checkanytable) (lua_State *L, int narg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API int (luaL_rometatable) (lua_State *L, const char* tname, void *p); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, + const char *const lst[]); + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +#if 0 +LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); +#else +LUALIB_API int (luaL_loadfsfile) (lua_State *L, const char *filename); +#endif +LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, + const char *name); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + + +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, + const char *r); + +LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx, + const char *fname, int szhint); + + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define luaL_argcheck(L, cond,numarg,extramsg) \ + ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#if 0 +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) +#else +#define luaL_dofile(L, fn) \ + (luaL_loadfsfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) +#endif + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + + +typedef struct luaL_Buffer { + char *p; /* current position in buffer */ + int lvl; /* number of strings in the stack (level) */ + lua_State *L; + char buffer[LUAL_BUFFERSIZE]; +} luaL_Buffer; + +#define luaL_addchar(B,c) \ + ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ + (*(B)->p++ = (char)(c))) + +/* compatibility only */ +#define luaL_putchar(B,c) luaL_addchar(B,c) + +#define luaL_addsize(B,n) ((B)->p += (n)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); + + +/* }====================================================== */ + + +/* compatibility with ref system */ + +/* pre-defined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \ + (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0)) + +#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref)) + +#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref)) + + +#define luaL_reg luaL_Reg + +#endif + + diff --git a/app/lua/lbaselib.c b/app/lua/lbaselib.c new file mode 100644 index 00000000..eb809673 --- /dev/null +++ b/app/lua/lbaselib.c @@ -0,0 +1,722 @@ +/* +** $Id: lbaselib.c,v 1.191.1.6 2008/02/14 16:46:22 roberto Exp $ +** Basic library +** See Copyright Notice in lua.h +*/ + + + +#include "c_ctype.h" +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_string.h" + +#define lbaselib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "lrotable.h" + + + + +/* +** If your system does not support `stdout', you can just remove this function. +** If you need, you can define your own `print' function, following this +** model but changing `fputs' to put the strings at a proper place +** (a console window or a log file, for instance). +*/ +static int ICACHE_FLASH_ATTR luaB_print (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + lua_getglobal(L, "tostring"); + for (i=1; i<=n; i++) { + const char *s; + lua_pushvalue(L, -1); /* function to be called */ + lua_pushvalue(L, i); /* value to print */ + lua_call(L, 1, 1); + s = lua_tostring(L, -1); /* get result */ + if (s == NULL) + return luaL_error(L, LUA_QL("tostring") " must return a string to " + LUA_QL("print")); +#if defined(LUA_USE_STDIO) + if (i>1) c_fputs("\t", c_stdout); + c_fputs(s, c_stdout); +#else + if (i>1) luai_writestring("\t", 1); + luai_writestring(s, c_strlen(s)); +#endif + lua_pop(L, 1); /* pop result */ + } +#if defined(LUA_USE_STDIO) + c_fputs("\n", c_stdout); +#else + luai_writeline(); +#endif + return 0; +} + + +static int ICACHE_FLASH_ATTR luaB_tonumber (lua_State *L) { + int base = luaL_optint(L, 2, 10); + if (base == 10) { /* standard conversion */ + luaL_checkany(L, 1); + if (lua_isnumber(L, 1)) { + lua_pushnumber(L, lua_tonumber(L, 1)); + return 1; + } + } + else { + const char *s1 = luaL_checkstring(L, 1); + char *s2; + unsigned long n; + luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); + n = c_strtoul(s1, &s2, base); + if (s1 != s2) { /* at least one valid digit? */ + while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ + if (*s2 == '\0') { /* no invalid trailing characters? */ + lua_pushnumber(L, (lua_Number)n); + return 1; + } + } + } + lua_pushnil(L); /* else not a number */ + return 1; +} + + +static int ICACHE_FLASH_ATTR luaB_error (lua_State *L) { + int level = luaL_optint(L, 2, 1); + lua_settop(L, 1); + if (lua_isstring(L, 1) && level > 0) { /* add extra information? */ + luaL_where(L, level); + lua_pushvalue(L, 1); + lua_concat(L, 2); + } + return lua_error(L); +} + + +static int ICACHE_FLASH_ATTR luaB_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); + return 1; /* no metatable */ + } + luaL_getmetafield(L, 1, "__metatable"); + return 1; /* returns either __metatable field (if present) or metatable */ +} + + +static int ICACHE_FLASH_ATTR luaB_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE || t == LUA_TROTABLE, 2, + "nil or table expected"); + if (luaL_getmetafield(L, 1, "__metatable")) + luaL_error(L, "cannot change a protected metatable"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; +} + + +static void ICACHE_FLASH_ATTR getfunc (lua_State *L, int opt) { + if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); + else { + lua_Debug ar; + int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1); + luaL_argcheck(L, level >= 0, 1, "level must be non-negative"); + if (lua_getstack(L, level, &ar) == 0) + luaL_argerror(L, 1, "invalid level"); + lua_getinfo(L, "f", &ar); + if (lua_isnil(L, -1)) + luaL_error(L, "no function environment for tail call at level %d", + level); + } +} + + +static int ICACHE_FLASH_ATTR luaB_getfenv (lua_State *L) { + getfunc(L, 1); + if (lua_iscfunction(L, -1)) /* is a C function? */ + lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */ + else + lua_getfenv(L, -1); + return 1; +} + + +static int ICACHE_FLASH_ATTR luaB_setfenv (lua_State *L) { + luaL_checktype(L, 2, LUA_TTABLE); + getfunc(L, 0); + lua_pushvalue(L, 2); + if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) { + /* change environment of current thread */ + lua_pushthread(L); + lua_insert(L, -2); + lua_setfenv(L, -2); + return 0; + } + else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0) + luaL_error(L, + LUA_QL("setfenv") " cannot change environment of given object"); + return 1; +} + + +static int ICACHE_FLASH_ATTR luaB_rawequal (lua_State *L) { + luaL_checkany(L, 1); + luaL_checkany(L, 2); + lua_pushboolean(L, lua_rawequal(L, 1, 2)); + return 1; +} + + +static int ICACHE_FLASH_ATTR luaB_rawget (lua_State *L) { + luaL_checkanytable(L, 1); + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_rawget(L, 1); + return 1; +} + +static int ICACHE_FLASH_ATTR luaB_rawset (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + luaL_checkany(L, 3); + lua_settop(L, 3); + lua_rawset(L, 1); + return 1; +} + + +static int ICACHE_FLASH_ATTR luaB_gcinfo (lua_State *L) { + lua_pushinteger(L, lua_getgccount(L)); + return 1; +} + + +static int ICACHE_FLASH_ATTR luaB_collectgarbage (lua_State *L) { + static const char *const opts[] = {"stop", "restart", "collect", + "count", "step", "setpause", "setstepmul","setmemlimit","getmemlimit", NULL}; + static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, + LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, + LUA_GCSETMEMLIMIT,LUA_GCGETMEMLIMIT}; + int o = luaL_checkoption(L, 1, "collect", opts); + int ex = luaL_optint(L, 2, 0); + int res = lua_gc(L, optsnum[o], ex); + switch (optsnum[o]) { + case LUA_GCCOUNT: { + int b = lua_gc(L, LUA_GCCOUNTB, 0); + lua_pushnumber(L, res + ((lua_Number)b/1024)); + return 1; + } + case LUA_GCSTEP: { + lua_pushboolean(L, res); + return 1; + } + default: { + lua_pushnumber(L, res); + return 1; + } + } +} + + +static int ICACHE_FLASH_ATTR luaB_type (lua_State *L) { + luaL_checkany(L, 1); + lua_pushstring(L, luaL_typename(L, 1)); + return 1; +} + + +static int ICACHE_FLASH_ATTR luaB_next (lua_State *L) { + luaL_checkanytable(L, 1); + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, 1)) + return 2; + else { + lua_pushnil(L); + return 1; + } +} + + +static int ICACHE_FLASH_ATTR luaB_pairs (lua_State *L) { + luaL_checkanytable(L, 1); + lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnil(L); /* and initial value */ + return 3; +} + + +static int ICACHE_FLASH_ATTR ipairsaux (lua_State *L) { + int i = luaL_checkint(L, 2); + luaL_checkanytable(L, 1); + i++; /* next value */ + lua_pushinteger(L, i); + lua_rawgeti(L, 1, i); + return (lua_isnil(L, -1)) ? 0 : 2; +} + + +static int ICACHE_FLASH_ATTR luaB_ipairs (lua_State *L) { + luaL_checkanytable(L, 1); + lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushinteger(L, 0); /* and initial value */ + return 3; +} + + +static int ICACHE_FLASH_ATTR load_aux (lua_State *L, int status) { + if (status == 0) /* OK? */ + return 1; + else { + lua_pushnil(L); + lua_insert(L, -2); /* put before error message */ + return 2; /* return nil plus error message */ + } +} + + +static int ICACHE_FLASH_ATTR luaB_loadstring (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + const char *chunkname = luaL_optstring(L, 2, s); + return load_aux(L, luaL_loadbuffer(L, s, l, chunkname)); +} + + +static int ICACHE_FLASH_ATTR luaB_loadfile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); +#if 0 + return load_aux(L, luaL_loadfile(L, fname)); +#else + return load_aux(L, luaL_loadfsfile(L, fname)); +#endif +} + + +/* +** Reader for generic `load' function: `lua_load' uses the +** stack for internal stuff, so the reader cannot change the +** stack top. Instead, it keeps its resulting string in a +** reserved slot inside the stack. +*/ +static const char *ICACHE_FLASH_ATTR generic_reader (lua_State *L, void *ud, size_t *size) { + (void)ud; /* to avoid warnings */ + if (L == NULL && size == NULL) // direct mode check, doesn't happen + return NULL; + luaL_checkstack(L, 2, "too many nested functions"); + lua_pushvalue(L, 1); /* get function */ + lua_call(L, 0, 1); /* call it */ + if (lua_isnil(L, -1)) { + *size = 0; + return NULL; + } + else if (lua_isstring(L, -1)) { + lua_replace(L, 3); /* save string in a reserved stack slot */ + return lua_tolstring(L, 3, size); + } + else luaL_error(L, "reader function must return a string"); + return NULL; /* to avoid warnings */ +} + + +static int ICACHE_FLASH_ATTR luaB_load (lua_State *L) { + int status; + const char *cname = luaL_optstring(L, 2, "=(load)"); + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, 3); /* function, eventual name, plus one reserved slot */ + status = lua_load(L, generic_reader, NULL, cname); + return load_aux(L, status); +} + + +static int ICACHE_FLASH_ATTR luaB_dofile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + int n = lua_gettop(L); +#if 0 + if (luaL_loadfile(L, fname) != 0) lua_error(L); +#else + if (luaL_loadfsfile(L, fname) != 0) lua_error(L); +#endif + lua_call(L, 0, LUA_MULTRET); + return lua_gettop(L) - n; +} + + +static int ICACHE_FLASH_ATTR luaB_assert (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_toboolean(L, 1)) + return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); + return lua_gettop(L); +} + + +static int ICACHE_FLASH_ATTR luaB_unpack (lua_State *L) { + int i, e, n; + luaL_checktype(L, 1, LUA_TTABLE); + i = luaL_optint(L, 2, 1); + e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1)); + if (i > e) return 0; /* empty range */ + n = e - i + 1; /* number of elements */ + if (n <= 0 || !lua_checkstack(L, n)) /* n <= 0 means arith. overflow */ + return luaL_error(L, "too many results to unpack"); + lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */ + while (i++ < e) /* push arg[i + 1...e] */ + lua_rawgeti(L, 1, i); + return n; +} + + +static int ICACHE_FLASH_ATTR luaB_select (lua_State *L) { + int n = lua_gettop(L); + if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') { + lua_pushinteger(L, n-1); + return 1; + } + else { + int i = luaL_checkint(L, 1); + if (i < 0) i = n + i; + else if (i > n) i = n; + luaL_argcheck(L, 1 <= i, 1, "index out of range"); + return n - i; + } +} + + +static int ICACHE_FLASH_ATTR luaB_pcall (lua_State *L) { + int status; + luaL_checkany(L, 1); + status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); + lua_pushboolean(L, (status == 0)); + lua_insert(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int ICACHE_FLASH_ATTR luaB_xpcall (lua_State *L) { + int status; + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_insert(L, 1); /* put error function under function to be called */ + status = lua_pcall(L, 0, LUA_MULTRET, 1); + lua_pushboolean(L, (status == 0)); + lua_replace(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int ICACHE_FLASH_ATTR luaB_tostring (lua_State *L) { + luaL_checkany(L, 1); + if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ + return 1; /* use its value */ + switch (lua_type(L, 1)) { + case LUA_TNUMBER: + lua_pushstring(L, lua_tostring(L, 1)); + break; + case LUA_TSTRING: + lua_pushvalue(L, 1); + break; + case LUA_TBOOLEAN: + lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); + break; + case LUA_TNIL: + lua_pushliteral(L, "nil"); + break; + default: + lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1)); + break; + } + return 1; +} + + +static int ICACHE_FLASH_ATTR luaB_newproxy (lua_State *L) { + lua_settop(L, 1); + lua_newuserdata(L, 0); /* create proxy */ + if (lua_toboolean(L, 1) == 0) + return 1; /* no metatable */ + else if (lua_isboolean(L, 1)) { + lua_newtable(L); /* create a new metatable `m' ... */ + lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */ + lua_pushboolean(L, 1); + lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */ + } + else { + int validproxy = 0; /* to check if weaktable[metatable(u)] == true */ + if (lua_getmetatable(L, 1)) { + lua_rawget(L, lua_upvalueindex(1)); + validproxy = lua_toboolean(L, -1); + lua_pop(L, 1); /* remove value */ + } + luaL_argcheck(L, validproxy, 1, "boolean or proxy expected"); + lua_getmetatable(L, 1); /* metatable is valid; get it */ + } + lua_setmetatable(L, 2); + return 1; +} + +#define LUA_BASELIB_FUNCLIST\ + {LSTRKEY("assert"), LFUNCVAL(luaB_assert)},\ + {LSTRKEY("collectgarbage"), LFUNCVAL(luaB_collectgarbage)},\ + {LSTRKEY("dofile"), LFUNCVAL(luaB_dofile)},\ + {LSTRKEY("error"), LFUNCVAL(luaB_error)},\ + {LSTRKEY("gcinfo"), LFUNCVAL(luaB_gcinfo)},\ + {LSTRKEY("getfenv"), LFUNCVAL(luaB_getfenv)},\ + {LSTRKEY("getmetatable"), LFUNCVAL(luaB_getmetatable)},\ + {LSTRKEY("loadfile"), LFUNCVAL(luaB_loadfile)},\ + {LSTRKEY("load"), LFUNCVAL(luaB_load)},\ + {LSTRKEY("loadstring"), LFUNCVAL(luaB_loadstring)},\ + {LSTRKEY("next"), LFUNCVAL(luaB_next)},\ + {LSTRKEY("pcall"), LFUNCVAL(luaB_pcall)},\ + {LSTRKEY("print"), LFUNCVAL(luaB_print)},\ + {LSTRKEY("rawequal"), LFUNCVAL(luaB_rawequal)},\ + {LSTRKEY("rawget"), LFUNCVAL(luaB_rawget)},\ + {LSTRKEY("rawset"), LFUNCVAL(luaB_rawset)},\ + {LSTRKEY("select"), LFUNCVAL(luaB_select)},\ + {LSTRKEY("setfenv"), LFUNCVAL(luaB_setfenv)},\ + {LSTRKEY("setmetatable"), LFUNCVAL(luaB_setmetatable)},\ + {LSTRKEY("tonumber"), LFUNCVAL(luaB_tonumber)},\ + {LSTRKEY("tostring"), LFUNCVAL(luaB_tostring)},\ + {LSTRKEY("type"), LFUNCVAL(luaB_type)},\ + {LSTRKEY("unpack"), LFUNCVAL(luaB_unpack)},\ + {LSTRKEY("xpcall"), LFUNCVAL(luaB_xpcall)} + +#if LUA_OPTIMIZE_MEMORY == 2 +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE base_funcs_list[] = { + LUA_BASELIB_FUNCLIST, + {LNILKEY, LNILVAL} +}; +#endif + + +static int ICACHE_FLASH_ATTR luaB_index(lua_State *L) { +#if LUA_OPTIMIZE_MEMORY == 2 + int fres; + if ((fres = luaR_findfunction(L, base_funcs_list)) != 0) + return fres; +#endif + const char *keyname = luaL_checkstring(L, 2); + if (!c_strcmp(keyname, "_VERSION")) { + lua_pushliteral(L, LUA_VERSION); + return 1; + } + void *res = luaR_findglobal(keyname, c_strlen(keyname)); + if (!res) + return 0; + else { + lua_pushrotable(L, res); + return 1; + } +} + +static const luaL_Reg base_funcs[] = { +#if LUA_OPTIMIZE_MEMORY != 2 +#undef MIN_OPT_LEVEL +#define MIN_OPT_LEVEL 0 +#include "lrodefs.h" + LUA_BASELIB_FUNCLIST, +#endif + {"__index", luaB_index}, + {NULL, NULL} +}; + + +/* +** {====================================================== +** Coroutine library +** ======================================================= +*/ + +#define CO_RUN 0 /* running */ +#define CO_SUS 1 /* suspended */ +#define CO_NOR 2 /* 'normal' (it resumed another coroutine) */ +#define CO_DEAD 3 + +static const char *const statnames[] = + {"running", "suspended", "normal", "dead"}; + +static int ICACHE_FLASH_ATTR costatus (lua_State *L, lua_State *co) { + if (L == co) return CO_RUN; + switch (lua_status(co)) { + case LUA_YIELD: + return CO_SUS; + case 0: { + lua_Debug ar; + if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ + return CO_NOR; /* it is running */ + else if (lua_gettop(co) == 0) + return CO_DEAD; + else + return CO_SUS; /* initial state */ + } + default: /* some error occured */ + return CO_DEAD; + } +} + + +static int ICACHE_FLASH_ATTR luaB_costatus (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argcheck(L, co, 1, "coroutine expected"); + lua_pushstring(L, statnames[costatus(L, co)]); + return 1; +} + + +static int ICACHE_FLASH_ATTR auxresume (lua_State *L, lua_State *co, int narg) { + int status = costatus(L, co); + if (!lua_checkstack(co, narg)) + luaL_error(L, "too many arguments to resume"); + if (status != CO_SUS) { + lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]); + return -1; /* error flag */ + } + lua_xmove(L, co, narg); + lua_setlevel(L, co); + status = lua_resume(co, narg); + if (status == 0 || status == LUA_YIELD) { + int nres = lua_gettop(co); + if (!lua_checkstack(L, nres + 1)) + luaL_error(L, "too many results to resume"); + lua_xmove(co, L, nres); /* move yielded values */ + return nres; + } + else { + lua_xmove(co, L, 1); /* move error message */ + return -1; /* error flag */ + } +} + + +static int ICACHE_FLASH_ATTR luaB_coresume (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + int r; + luaL_argcheck(L, co, 1, "coroutine expected"); + r = auxresume(L, co, lua_gettop(L) - 1); + if (r < 0) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; /* return false + error message */ + } + else { + lua_pushboolean(L, 1); + lua_insert(L, -(r + 1)); + return r + 1; /* return true + `resume' returns */ + } +} + + +static int ICACHE_FLASH_ATTR luaB_auxwrap (lua_State *L) { + lua_State *co = lua_tothread(L, lua_upvalueindex(1)); + int r = auxresume(L, co, lua_gettop(L)); + if (r < 0) { + if (lua_isstring(L, -1)) { /* error object is a string? */ + luaL_where(L, 1); /* add extra info */ + lua_insert(L, -2); + lua_concat(L, 2); + } + lua_error(L); /* propagate error */ + } + return r; +} + + +static int ICACHE_FLASH_ATTR luaB_cocreate (lua_State *L) { + lua_State *NL = lua_newthread(L); + luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, + "Lua function expected"); + lua_pushvalue(L, 1); /* move function to top */ + lua_xmove(L, NL, 1); /* move function from L to NL */ + return 1; +} + + +static int ICACHE_FLASH_ATTR luaB_cowrap (lua_State *L) { + luaB_cocreate(L); + lua_pushcclosure(L, luaB_auxwrap, 1); + return 1; +} + + +static int ICACHE_FLASH_ATTR luaB_yield (lua_State *L) { + return lua_yield(L, lua_gettop(L)); +} + + +static int ICACHE_FLASH_ATTR luaB_corunning (lua_State *L) { + if (lua_pushthread(L)) + lua_pushnil(L); /* main thread is not a coroutine */ + return 1; +} + +#undef MIN_OPT_LEVEL +#define MIN_OPT_LEVEL 1 +#include "lrodefs.h" +const LUA_REG_TYPE co_funcs[] = { + {LSTRKEY("create"), LFUNCVAL(luaB_cocreate)}, + {LSTRKEY("resume"), LFUNCVAL(luaB_coresume)}, + {LSTRKEY("running"), LFUNCVAL(luaB_corunning)}, + {LSTRKEY("status"), LFUNCVAL(luaB_costatus)}, + {LSTRKEY("wrap"), LFUNCVAL(luaB_cowrap)}, + {LSTRKEY("yield"), LFUNCVAL(luaB_yield)}, + {LNILKEY, LNILVAL} +}; + +/* }====================================================== */ + + +static void ICACHE_FLASH_ATTR auxopen (lua_State *L, const char *name, + lua_CFunction f, lua_CFunction u) { + lua_pushcfunction(L, u); + lua_pushcclosure(L, f, 1); + lua_setfield(L, -2, name); +} + + +static void ICACHE_FLASH_ATTR base_open (lua_State *L) { + /* set global _G */ + lua_pushvalue(L, LUA_GLOBALSINDEX); + lua_setglobal(L, "_G"); + /* open lib into global table */ + luaL_register_light(L, "_G", base_funcs); +#if LUA_OPTIMIZE_MEMORY > 0 + lua_pushvalue(L, -1); + lua_setmetatable(L, -2); +#else + lua_pushliteral(L, LUA_VERSION); + lua_setglobal(L, "_VERSION"); /* set global _VERSION */ +#endif + /* `ipairs' and `pairs' need auxliliary functions as upvalues */ + auxopen(L, "ipairs", luaB_ipairs, ipairsaux); + auxopen(L, "pairs", luaB_pairs, luaB_next); + /* `newproxy' needs a weaktable as upvalue */ + lua_createtable(L, 0, 1); /* new table `w' */ + lua_pushvalue(L, -1); /* `w' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "kv"); + lua_setfield(L, -2, "__mode"); /* metatable(w).__mode = "kv" */ + lua_pushcclosure(L, luaB_newproxy, 1); + lua_setglobal(L, "newproxy"); /* set global `newproxy' */ +} + + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_base (lua_State *L) { + base_open(L); +#if LUA_OPTIMIZE_MEMORY == 0 + luaL_register(L, LUA_COLIBNAME, co_funcs); + return 2; +#else + return 1; +#endif +} diff --git a/app/lua/lcode.c b/app/lua/lcode.c new file mode 100644 index 00000000..64b8bb36 --- /dev/null +++ b/app/lua/lcode.c @@ -0,0 +1,831 @@ +/* +** $Id: lcode.c,v 2.25.1.5 2011/01/31 14:53:16 roberto Exp $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + + +#include "c_stdlib.h" + +#define lcode_c +#define LUA_CORE + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "ltable.h" + + +#define hasjumps(e) ((e)->t != (e)->f) + + +static int ICACHE_FLASH_ATTR isnumeral(expdesc *e) { + return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP); +} + + +void ICACHE_FLASH_ATTR luaK_nil (FuncState *fs, int from, int n) { + Instruction *previous; + if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ + if (fs->pc == 0) { /* function start? */ + if (from >= fs->nactvar) + return; /* positions are already clean */ + } + else { + previous = &fs->f->code[fs->pc-1]; + if (GET_OPCODE(*previous) == OP_LOADNIL) { + int pfrom = GETARG_A(*previous); + int pto = GETARG_B(*previous); + if (pfrom <= from && from <= pto+1) { /* can connect both? */ + if (from+n-1 > pto) + SETARG_B(*previous, from+n-1); + return; + } + } + } + } + luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0); /* else no optimization */ +} + + +int ICACHE_FLASH_ATTR luaK_jump (FuncState *fs) { + int jpc = fs->jpc; /* save list of jumps to here */ + int j; + fs->jpc = NO_JUMP; + j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP); + luaK_concat(fs, &j, jpc); /* keep them on hold */ + return j; +} + + +void ICACHE_FLASH_ATTR luaK_ret (FuncState *fs, int first, int nret) { + luaK_codeABC(fs, OP_RETURN, first, nret+1, 0); +} + + +static int ICACHE_FLASH_ATTR condjump (FuncState *fs, OpCode op, int A, int B, int C) { + luaK_codeABC(fs, op, A, B, C); + return luaK_jump(fs); +} + + +static void ICACHE_FLASH_ATTR fixjump (FuncState *fs, int pc, int dest) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest-(pc+1); + lua_assert(dest != NO_JUMP); + if (c_abs(offset) > MAXARG_sBx) + luaX_syntaxerror(fs->ls, "control structure too long"); + SETARG_sBx(*jmp, offset); +} + + +/* +** returns current `pc' and marks it as a jump target (to avoid wrong +** optimizations with consecutive instructions not in the same basic block). +*/ +int ICACHE_FLASH_ATTR luaK_getlabel (FuncState *fs) { + fs->lasttarget = fs->pc; + return fs->pc; +} + + +static int ICACHE_FLASH_ATTR getjump (FuncState *fs, int pc) { + int offset = GETARG_sBx(fs->f->code[pc]); + if (offset == NO_JUMP) /* point to itself represents end of list */ + return NO_JUMP; /* end of list */ + else + return (pc+1)+offset; /* turn offset into absolute position */ +} + + +static Instruction *ICACHE_FLASH_ATTR getjumpcontrol (FuncState *fs, int pc) { + Instruction *pi = &fs->f->code[pc]; + if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) + return pi-1; + else + return pi; +} + + +/* +** check whether list has any jump that do not produce a value +** (or produce an inverted value) +*/ +static int ICACHE_FLASH_ATTR need_value (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) { + Instruction i = *getjumpcontrol(fs, list); + if (GET_OPCODE(i) != OP_TESTSET) return 1; + } + return 0; /* not found */ +} + + +static int ICACHE_FLASH_ATTR patchtestreg (FuncState *fs, int node, int reg) { + Instruction *i = getjumpcontrol(fs, node); + if (GET_OPCODE(*i) != OP_TESTSET) + return 0; /* cannot patch other instructions */ + if (reg != NO_REG && reg != GETARG_B(*i)) + SETARG_A(*i, reg); + else /* no register to put value or register already has the value */ + *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); + + return 1; +} + + +static void ICACHE_FLASH_ATTR removevalues (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) + patchtestreg(fs, list, NO_REG); +} + + +static void ICACHE_FLASH_ATTR patchlistaux (FuncState *fs, int list, int vtarget, int reg, + int dtarget) { + while (list != NO_JUMP) { + int next = getjump(fs, list); + if (patchtestreg(fs, list, reg)) + fixjump(fs, list, vtarget); + else + fixjump(fs, list, dtarget); /* jump to default target */ + list = next; + } +} + + +static void ICACHE_FLASH_ATTR dischargejpc (FuncState *fs) { + patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); + fs->jpc = NO_JUMP; +} + + +void ICACHE_FLASH_ATTR luaK_patchlist (FuncState *fs, int list, int target) { + if (target == fs->pc) + luaK_patchtohere(fs, list); + else { + lua_assert(target < fs->pc); + patchlistaux(fs, list, target, NO_REG, target); + } +} + + +void ICACHE_FLASH_ATTR luaK_patchtohere (FuncState *fs, int list) { + luaK_getlabel(fs); + luaK_concat(fs, &fs->jpc, list); +} + + +void ICACHE_FLASH_ATTR luaK_concat (FuncState *fs, int *l1, int l2) { + if (l2 == NO_JUMP) return; + else if (*l1 == NO_JUMP) + *l1 = l2; + else { + int list = *l1; + int next; + while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ + list = next; + fixjump(fs, list, l2); + } +} + + +void ICACHE_FLASH_ATTR luaK_checkstack (FuncState *fs, int n) { + int newstack = fs->freereg + n; + if (newstack > fs->f->maxstacksize) { + if (newstack >= MAXSTACK) + luaX_syntaxerror(fs->ls, "function or expression too complex"); + fs->f->maxstacksize = cast_byte(newstack); + } +} + + +void ICACHE_FLASH_ATTR luaK_reserveregs (FuncState *fs, int n) { + luaK_checkstack(fs, n); + fs->freereg += n; +} + + +static void ICACHE_FLASH_ATTR freereg (FuncState *fs, int reg) { + if (!ISK(reg) && reg >= fs->nactvar) { + fs->freereg--; + lua_assert(reg == fs->freereg); + } +} + + +static void ICACHE_FLASH_ATTR freeexp (FuncState *fs, expdesc *e) { + if (e->k == VNONRELOC) + freereg(fs, e->u.s.info); +} + + +static int ICACHE_FLASH_ATTR addk (FuncState *fs, TValue *k, TValue *v) { + lua_State *L = fs->L; + TValue *idx = luaH_set(L, fs->h, k); + Proto *f = fs->f; + int oldsize = f->sizek; + if (ttisnumber(idx)) { + lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v)); + return cast_int(nvalue(idx)); + } + else { /* constant not found; create a new entry */ + setnvalue(idx, cast_num(fs->nk)); + luaM_growvector(L, f->k, fs->nk, f->sizek, TValue, + MAXARG_Bx, "constant table overflow"); + while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); + setobj(L, &f->k[fs->nk], v); + luaC_barrier(L, f, v); + return fs->nk++; + } +} + + +int ICACHE_FLASH_ATTR luaK_stringK (FuncState *fs, TString *s) { + TValue o; + setsvalue(fs->L, &o, s); + return addk(fs, &o, &o); +} + + +int ICACHE_FLASH_ATTR luaK_numberK (FuncState *fs, lua_Number r) { + TValue o; + setnvalue(&o, r); + return addk(fs, &o, &o); +} + + +static int ICACHE_FLASH_ATTR boolK (FuncState *fs, int b) { + TValue o; + setbvalue(&o, b); + return addk(fs, &o, &o); +} + + +static int ICACHE_FLASH_ATTR nilK (FuncState *fs) { + TValue k, v; + setnilvalue(&v); + /* cannot use nil as key; instead use table itself to represent nil */ + sethvalue(fs->L, &k, fs->h); + return addk(fs, &k, &v); +} + + +void ICACHE_FLASH_ATTR luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { + if (e->k == VCALL) { /* expression is an open function call? */ + SETARG_C(getcode(fs, e), nresults+1); + } + else if (e->k == VVARARG) { + SETARG_B(getcode(fs, e), nresults+1); + SETARG_A(getcode(fs, e), fs->freereg); + luaK_reserveregs(fs, 1); + } +} + + +void ICACHE_FLASH_ATTR luaK_setoneret (FuncState *fs, expdesc *e) { + if (e->k == VCALL) { /* expression is an open function call? */ + e->k = VNONRELOC; + e->u.s.info = GETARG_A(getcode(fs, e)); + } + else if (e->k == VVARARG) { + SETARG_B(getcode(fs, e), 2); + e->k = VRELOCABLE; /* can relocate its simple result */ + } +} + + +void ICACHE_FLASH_ATTR luaK_dischargevars (FuncState *fs, expdesc *e) { + switch (e->k) { + case VLOCAL: { + e->k = VNONRELOC; + break; + } + case VUPVAL: { + e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0); + e->k = VRELOCABLE; + break; + } + case VGLOBAL: { + e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info); + e->k = VRELOCABLE; + break; + } + case VINDEXED: { + freereg(fs, e->u.s.aux); + freereg(fs, e->u.s.info); + e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux); + e->k = VRELOCABLE; + break; + } + case VVARARG: + case VCALL: { + luaK_setoneret(fs, e); + break; + } + default: break; /* there is one value available (somewhere) */ + } +} + + +static int ICACHE_FLASH_ATTR code_label (FuncState *fs, int A, int b, int jump) { + luaK_getlabel(fs); /* those instructions may be jump targets */ + return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump); +} + + +static void ICACHE_FLASH_ATTR discharge2reg (FuncState *fs, expdesc *e, int reg) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: { + luaK_nil(fs, reg, 1); + break; + } + case VFALSE: case VTRUE: { + luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); + break; + } + case VK: { + luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info); + break; + } + case VKNUM: { + luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval)); + break; + } + case VRELOCABLE: { + Instruction *pc = &getcode(fs, e); + SETARG_A(*pc, reg); + break; + } + case VNONRELOC: { + if (reg != e->u.s.info) + luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0); + break; + } + default: { + lua_assert(e->k == VVOID || e->k == VJMP); + return; /* nothing to do... */ + } + } + e->u.s.info = reg; + e->k = VNONRELOC; +} + + +static void ICACHE_FLASH_ATTR discharge2anyreg (FuncState *fs, expdesc *e) { + if (e->k != VNONRELOC) { + luaK_reserveregs(fs, 1); + discharge2reg(fs, e, fs->freereg-1); + } +} + + +static void ICACHE_FLASH_ATTR exp2reg (FuncState *fs, expdesc *e, int reg) { + discharge2reg(fs, e, reg); + if (e->k == VJMP) + luaK_concat(fs, &e->t, e->u.s.info); /* put this jump in `t' list */ + if (hasjumps(e)) { + int final; /* position after whole expression */ + int p_f = NO_JUMP; /* position of an eventual LOAD false */ + int p_t = NO_JUMP; /* position of an eventual LOAD true */ + if (need_value(fs, e->t) || need_value(fs, e->f)) { + int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); + p_f = code_label(fs, reg, 0, 1); + p_t = code_label(fs, reg, 1, 0); + luaK_patchtohere(fs, fj); + } + final = luaK_getlabel(fs); + patchlistaux(fs, e->f, final, reg, p_f); + patchlistaux(fs, e->t, final, reg, p_t); + } + e->f = e->t = NO_JUMP; + e->u.s.info = reg; + e->k = VNONRELOC; +} + + +void ICACHE_FLASH_ATTR luaK_exp2nextreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + freeexp(fs, e); + luaK_reserveregs(fs, 1); + exp2reg(fs, e, fs->freereg - 1); +} + + +int ICACHE_FLASH_ATTR luaK_exp2anyreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + if (e->k == VNONRELOC) { + if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */ + if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */ + exp2reg(fs, e, e->u.s.info); /* put value on it */ + return e->u.s.info; + } + } + luaK_exp2nextreg(fs, e); /* default */ + return e->u.s.info; +} + + +void ICACHE_FLASH_ATTR luaK_exp2val (FuncState *fs, expdesc *e) { + if (hasjumps(e)) + luaK_exp2anyreg(fs, e); + else + luaK_dischargevars(fs, e); +} + + +int ICACHE_FLASH_ATTR luaK_exp2RK (FuncState *fs, expdesc *e) { + luaK_exp2val(fs, e); + switch (e->k) { + case VKNUM: + case VTRUE: + case VFALSE: + case VNIL: { + if (fs->nk <= MAXINDEXRK) { /* constant fit in RK operand? */ + e->u.s.info = (e->k == VNIL) ? nilK(fs) : + (e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) : + boolK(fs, (e->k == VTRUE)); + e->k = VK; + return RKASK(e->u.s.info); + } + else break; + } + case VK: { + if (e->u.s.info <= MAXINDEXRK) /* constant fit in argC? */ + return RKASK(e->u.s.info); + else break; + } + default: break; + } + /* not a constant in the right range: put it in a register */ + return luaK_exp2anyreg(fs, e); +} + + +void ICACHE_FLASH_ATTR luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { + switch (var->k) { + case VLOCAL: { + freeexp(fs, ex); + exp2reg(fs, ex, var->u.s.info); + return; + } + case VUPVAL: { + int e = luaK_exp2anyreg(fs, ex); + luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0); + break; + } + case VGLOBAL: { + int e = luaK_exp2anyreg(fs, ex); + luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info); + break; + } + case VINDEXED: { + int e = luaK_exp2RK(fs, ex); + luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e); + break; + } + default: { + lua_assert(0); /* invalid var kind to store */ + break; + } + } + freeexp(fs, ex); +} + + +void ICACHE_FLASH_ATTR luaK_self (FuncState *fs, expdesc *e, expdesc *key) { + int func; + luaK_exp2anyreg(fs, e); + freeexp(fs, e); + func = fs->freereg; + luaK_reserveregs(fs, 2); + luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key)); + freeexp(fs, key); + e->u.s.info = func; + e->k = VNONRELOC; +} + + +static void ICACHE_FLASH_ATTR invertjump (FuncState *fs, expdesc *e) { + Instruction *pc = getjumpcontrol(fs, e->u.s.info); + lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && + GET_OPCODE(*pc) != OP_TEST); + SETARG_A(*pc, !(GETARG_A(*pc))); +} + + +static int ICACHE_FLASH_ATTR jumponcond (FuncState *fs, expdesc *e, int cond) { + if (e->k == VRELOCABLE) { + Instruction ie = getcode(fs, e); + if (GET_OPCODE(ie) == OP_NOT) { + fs->pc--; /* remove previous OP_NOT */ + return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond); + } + /* else go through */ + } + discharge2anyreg(fs, e); + freeexp(fs, e); + return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond); +} + + +void ICACHE_FLASH_ATTR luaK_goiftrue (FuncState *fs, expdesc *e) { + int pc; /* pc of last jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VK: case VKNUM: case VTRUE: { + pc = NO_JUMP; /* always true; do nothing */ + break; + } + case VJMP: { + invertjump(fs, e); + pc = e->u.s.info; + break; + } + default: { + pc = jumponcond(fs, e, 0); + break; + } + } + luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */ + luaK_patchtohere(fs, e->t); + e->t = NO_JUMP; +} + + +static void ICACHE_FLASH_ATTR luaK_goiffalse (FuncState *fs, expdesc *e) { + int pc; /* pc of last jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: case VFALSE: { + pc = NO_JUMP; /* always false; do nothing */ + break; + } + case VJMP: { + pc = e->u.s.info; + break; + } + default: { + pc = jumponcond(fs, e, 1); + break; + } + } + luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */ + luaK_patchtohere(fs, e->f); + e->f = NO_JUMP; +} + + +static void ICACHE_FLASH_ATTR codenot (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: case VFALSE: { + e->k = VTRUE; + break; + } + case VK: case VKNUM: case VTRUE: { + e->k = VFALSE; + break; + } + case VJMP: { + invertjump(fs, e); + break; + } + case VRELOCABLE: + case VNONRELOC: { + discharge2anyreg(fs, e); + freeexp(fs, e); + e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0); + e->k = VRELOCABLE; + break; + } + default: { + lua_assert(0); /* cannot happen */ + break; + } + } + /* interchange true and false lists */ + { int temp = e->f; e->f = e->t; e->t = temp; } + removevalues(fs, e->f); + removevalues(fs, e->t); +} + + +void ICACHE_FLASH_ATTR luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { + t->u.s.aux = luaK_exp2RK(fs, k); + t->k = VINDEXED; +} + + +static int ICACHE_FLASH_ATTR constfolding (OpCode op, expdesc *e1, expdesc *e2) { + lua_Number v1, v2, r; + if (!isnumeral(e1) || !isnumeral(e2)) return 0; + v1 = e1->u.nval; + v2 = e2->u.nval; + switch (op) { + case OP_ADD: r = luai_numadd(v1, v2); break; + case OP_SUB: r = luai_numsub(v1, v2); break; + case OP_MUL: r = luai_nummul(v1, v2); break; + case OP_DIV: + if (v2 == 0) return 0; /* do not attempt to divide by 0 */ + r = luai_numdiv(v1, v2); break; + case OP_MOD: + if (v2 == 0) return 0; /* do not attempt to divide by 0 */ + r = luai_nummod(v1, v2); break; + case OP_POW: r = luai_numpow(v1, v2); break; + case OP_UNM: r = luai_numunm(v1); break; + case OP_LEN: return 0; /* no constant folding for 'len' */ + default: lua_assert(0); r = 0; break; + } + if (luai_numisnan(r)) return 0; /* do not attempt to produce NaN */ + e1->u.nval = r; + return 1; +} + + +static void ICACHE_FLASH_ATTR codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) { + if (constfolding(op, e1, e2)) + return; + else { + int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0; + int o1 = luaK_exp2RK(fs, e1); + if (o1 > o2) { + freeexp(fs, e1); + freeexp(fs, e2); + } + else { + freeexp(fs, e2); + freeexp(fs, e1); + } + e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2); + e1->k = VRELOCABLE; + } +} + + +static void ICACHE_FLASH_ATTR codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1, + expdesc *e2) { + int o1 = luaK_exp2RK(fs, e1); + int o2 = luaK_exp2RK(fs, e2); + freeexp(fs, e2); + freeexp(fs, e1); + if (cond == 0 && op != OP_EQ) { + int temp; /* exchange args to replace by `<' or `<=' */ + temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */ + cond = 1; + } + e1->u.s.info = condjump(fs, op, cond, o1, o2); + e1->k = VJMP; +} + + +void ICACHE_FLASH_ATTR luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) { + expdesc e2; + e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0; + switch (op) { + case OPR_MINUS: { + if (!isnumeral(e)) + luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */ + codearith(fs, OP_UNM, e, &e2); + break; + } + case OPR_NOT: codenot(fs, e); break; + case OPR_LEN: { + luaK_exp2anyreg(fs, e); /* cannot operate on constants */ + codearith(fs, OP_LEN, e, &e2); + break; + } + default: lua_assert(0); + } +} + + +void ICACHE_FLASH_ATTR luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { + switch (op) { + case OPR_AND: { + luaK_goiftrue(fs, v); + break; + } + case OPR_OR: { + luaK_goiffalse(fs, v); + break; + } + case OPR_CONCAT: { + luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */ + break; + } + case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: + case OPR_MOD: case OPR_POW: { + if (!isnumeral(v)) luaK_exp2RK(fs, v); + break; + } + default: { + luaK_exp2RK(fs, v); + break; + } + } +} + + +void ICACHE_FLASH_ATTR luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) { + switch (op) { + case OPR_AND: { + lua_assert(e1->t == NO_JUMP); /* list must be closed */ + luaK_dischargevars(fs, e2); + luaK_concat(fs, &e2->f, e1->f); + *e1 = *e2; + break; + } + case OPR_OR: { + lua_assert(e1->f == NO_JUMP); /* list must be closed */ + luaK_dischargevars(fs, e2); + luaK_concat(fs, &e2->t, e1->t); + *e1 = *e2; + break; + } + case OPR_CONCAT: { + luaK_exp2val(fs, e2); + if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) { + lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1); + freeexp(fs, e1); + SETARG_B(getcode(fs, e2), e1->u.s.info); + e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info; + } + else { + luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ + codearith(fs, OP_CONCAT, e1, e2); + } + break; + } + case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break; + case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break; + case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break; + case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break; + case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break; + case OPR_POW: codearith(fs, OP_POW, e1, e2); break; + case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break; + case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break; + case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break; + case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break; + case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break; + case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break; + default: lua_assert(0); + } +} + + +void ICACHE_FLASH_ATTR luaK_fixline (FuncState *fs, int line) { + fs->f->lineinfo[fs->pc - 1] = line; +} + + +static int ICACHE_FLASH_ATTR luaK_code (FuncState *fs, Instruction i, int line) { + Proto *f = fs->f; + dischargejpc(fs); /* `pc' will change */ + /* put new instruction in code array */ + luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction, + MAX_INT, "code size overflow"); + f->code[fs->pc] = i; + /* save corresponding line information */ + luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int, + MAX_INT, "code size overflow"); + f->lineinfo[fs->pc] = line; + return fs->pc++; +} + + +int ICACHE_FLASH_ATTR luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) { + lua_assert(getOpMode(o) == iABC); + lua_assert(getBMode(o) != OpArgN || b == 0); + lua_assert(getCMode(o) != OpArgN || c == 0); + return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline); +} + + +int ICACHE_FLASH_ATTR luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { + lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); + lua_assert(getCMode(o) == OpArgN); + return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline); +} + + +void ICACHE_FLASH_ATTR luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { + int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1; + int b = (tostore == LUA_MULTRET) ? 0 : tostore; + lua_assert(tostore != 0); + if (c <= MAXARG_C) + luaK_codeABC(fs, OP_SETLIST, base, b, c); + else { + luaK_codeABC(fs, OP_SETLIST, base, b, 0); + luaK_code(fs, cast(Instruction, c), fs->ls->lastline); + } + fs->freereg = base + 1; /* free registers with list values */ +} + diff --git a/app/lua/lcode.h b/app/lua/lcode.h new file mode 100644 index 00000000..b941c607 --- /dev/null +++ b/app/lua/lcode.h @@ -0,0 +1,76 @@ +/* +** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lcode_h +#define lcode_h + +#include "llex.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" + + +/* +** Marks the end of a patch list. It is an invalid value both as an absolute +** address, and as a list link (would link an element to itself). +*/ +#define NO_JUMP (-1) + + +/* +** grep "ORDER OPR" if you change these enums +*/ +typedef enum BinOpr { + OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, + OPR_CONCAT, + OPR_NE, OPR_EQ, + OPR_LT, OPR_LE, OPR_GT, OPR_GE, + OPR_AND, OPR_OR, + OPR_NOBINOPR +} BinOpr; + + +typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; + + +#define getcode(fs,e) ((fs)->f->code[(e)->u.s.info]) + +#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx) + +#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) + +LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); +LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C); +LUAI_FUNC void luaK_fixline (FuncState *fs, int line); +LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); +LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); +LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); +LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s); +LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r); +LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); +LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); +LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); +LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); +LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_jump (FuncState *fs); +LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); +LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); +LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); +LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); +LUAI_FUNC int luaK_getlabel (FuncState *fs); +LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v); +LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); +LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2); +LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); + + +#endif diff --git a/app/lua/ldebug.c b/app/lua/ldebug.c new file mode 100644 index 00000000..971f7331 --- /dev/null +++ b/app/lua/ldebug.c @@ -0,0 +1,649 @@ +/* +** $Id: ldebug.c,v 2.29.1.6 2008/05/08 16:56:26 roberto Exp $ +** Debug Interface +** See Copyright Notice in lua.h +*/ + + +#include "c_stdarg.h" +#include "c_stddef.h" +#include "c_string.h" + + +#define ldebug_c +#define LUA_CORE + +#include "lua.h" + +#include "lapi.h" +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + + +static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name); + + +static int ICACHE_FLASH_ATTR currentpc (lua_State *L, CallInfo *ci) { + if (!isLua(ci)) return -1; /* function is not a Lua function? */ + if (ci == L->ci) + ci->savedpc = L->savedpc; + return pcRel(ci->savedpc, ci_func(ci)->l.p); +} + + +static int ICACHE_FLASH_ATTR currentline (lua_State *L, CallInfo *ci) { + int pc = currentpc(L, ci); + if (pc < 0) + return -1; /* only active lua functions have current-line information */ + else + return getline(ci_func(ci)->l.p, pc); +} + + +/* +** this function can be called asynchronous (e.g. during a signal) +*/ +LUA_API int ICACHE_FLASH_ATTR lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { + if (func == NULL || mask == 0) { /* turn off hooks? */ + mask = 0; + func = NULL; + } + L->hook = func; + L->basehookcount = count; + resethookcount(L); + L->hookmask = cast_byte(mask); + return 1; +} + + +LUA_API lua_Hook ICACHE_FLASH_ATTR lua_gethook (lua_State *L) { + return L->hook; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_gethookmask (lua_State *L) { + return L->hookmask; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_gethookcount (lua_State *L) { + return L->basehookcount; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_getstack (lua_State *L, int level, lua_Debug *ar) { + int status; + CallInfo *ci; + lua_lock(L); + for (ci = L->ci; level > 0 && ci > L->base_ci; ci--) { + level--; + if (f_isLua(ci)) /* Lua function? */ + level -= ci->tailcalls; /* skip lost tail calls */ + } + if (level == 0 && ci > L->base_ci) { /* level found? */ + status = 1; + ar->i_ci = cast_int(ci - L->base_ci); + } + else if (level < 0) { /* level is of a lost tail call? */ + status = 1; + ar->i_ci = 0; + } + else status = 0; /* no such level */ + lua_unlock(L); + return status; +} + + +static Proto *ICACHE_FLASH_ATTR getluaproto (CallInfo *ci) { + return (isLua(ci) ? ci_func(ci)->l.p : NULL); +} + + +static const char *ICACHE_FLASH_ATTR findlocal (lua_State *L, CallInfo *ci, int n) { + const char *name; + Proto *fp = getluaproto(ci); + if (fp && (name = luaF_getlocalname(fp, n, currentpc(L, ci))) != NULL) + return name; /* is a local variable in a Lua function */ + else { + StkId limit = (ci == L->ci) ? L->top : (ci+1)->func; + if (limit - ci->base >= n && n > 0) /* is 'n' inside 'ci' stack? */ + return "(*temporary)"; + else + return NULL; + } +} + + +LUA_API const char *ICACHE_FLASH_ATTR lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { + CallInfo *ci = L->base_ci + ar->i_ci; + const char *name = findlocal(L, ci, n); + lua_lock(L); + if (name) + luaA_pushobject(L, ci->base + (n - 1)); + lua_unlock(L); + return name; +} + + +LUA_API const char *ICACHE_FLASH_ATTR lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { + CallInfo *ci = L->base_ci + ar->i_ci; + const char *name = findlocal(L, ci, n); + lua_lock(L); + if (name) + setobjs2s(L, ci->base + (n - 1), L->top - 1); + L->top--; /* pop value */ + lua_unlock(L); + return name; +} + + +static void ICACHE_FLASH_ATTR funcinfo (lua_Debug *ar, Closure *cl, void *plight) { + if (plight || cl->c.isC) { + ar->source = "=[C]"; + ar->linedefined = -1; + ar->lastlinedefined = -1; + ar->what = "C"; + } + else { + ar->source = getstr(cl->l.p->source); + ar->linedefined = cl->l.p->linedefined; + ar->lastlinedefined = cl->l.p->lastlinedefined; + ar->what = (ar->linedefined == 0) ? "main" : "Lua"; + } + luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); +} + + +static void ICACHE_FLASH_ATTR info_tailcall (lua_Debug *ar) { + ar->name = ar->namewhat = ""; + ar->what = "tail"; + ar->lastlinedefined = ar->linedefined = ar->currentline = -1; + ar->source = "=(tail call)"; + luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); + ar->nups = 0; +} + + +static void ICACHE_FLASH_ATTR collectvalidlines (lua_State *L, Closure *f) { + if (f == NULL || f->c.isC) { + setnilvalue(L->top); + } + else { + Table *t = luaH_new(L, 0, 0); + int *lineinfo = f->l.p->lineinfo; + int i; + for (i=0; il.p->sizelineinfo; i++) + setbvalue(luaH_setnum(L, t, lineinfo[i]), 1); + sethvalue(L, L->top, t); + } + incr_top(L); +} + + +static int ICACHE_FLASH_ATTR auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, + Closure *f, void *plight, CallInfo *ci) { + int status = 1; + if (plight == NULL && f == NULL) { + info_tailcall(ar); + return status; + } + for (; *what; what++) { + switch (*what) { + case 'S': { + funcinfo(ar, f, plight); + break; + } + case 'l': { + ar->currentline = (ci) ? currentline(L, ci) : -1; + break; + } + case 'u': { + ar->nups = f ? f->c.nupvalues : 0; + break; + } + case 'n': { + ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL; + if (ar->namewhat == NULL) { + ar->namewhat = ""; /* not found */ + ar->name = NULL; + } + break; + } + case 'L': + case 'f': /* handled by lua_getinfo */ + break; + default: status = 0; /* invalid option */ + } + } + return status; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { + int status; + Closure *f = NULL; + CallInfo *ci = NULL; + void *plight = NULL; + lua_lock(L); + if (*what == '>') { + StkId func = L->top - 1; + luai_apicheck(L, ttisfunction(func) || ttislightfunction(func)); + what++; /* skip the '>' */ + if (ttisfunction(func)) + f = clvalue(func); + else + plight = fvalue(func); + L->top--; /* pop function */ + } + else if (ar->i_ci != 0) { /* no tail call? */ + ci = L->base_ci + ar->i_ci; + lua_assert(ttisfunction(ci->func) || ttislightfunction(ci->func)); + if (ttisfunction(ci->func)) + f = clvalue(ci->func); + else + plight = fvalue(ci->func); + } + status = auxgetinfo(L, what, ar, f, plight, ci); + if (c_strchr(what, 'f')) { + if (f != NULL) + setclvalue(L, L->top, f) + else if (plight != NULL) + setfvalue(L->top, plight) + else + setnilvalue(L->top); + incr_top(L); + } + if (c_strchr(what, 'L')) + collectvalidlines(L, f); + lua_unlock(L); + return status; +} + + +/* +** {====================================================== +** Symbolic Execution and code checker +** ======================================================= +*/ + +#define check(x) if (!(x)) return 0; + +#define checkjump(pt,pc) check(0 <= pc && pc < pt->sizecode) + +#define checkreg(pt,reg) check((reg) < (pt)->maxstacksize) + + + +static int ICACHE_FLASH_ATTR precheck (const Proto *pt) { + check(pt->maxstacksize <= MAXSTACK); + check(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize); + check(!(pt->is_vararg & VARARG_NEEDSARG) || + (pt->is_vararg & VARARG_HASARG)); + check(pt->sizeupvalues <= pt->nups); + check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0); + check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN); + return 1; +} + + +#define checkopenop(pt,pc) luaG_checkopenop((pt)->code[(pc)+1]) + +int ICACHE_FLASH_ATTR luaG_checkopenop (Instruction i) { + switch (GET_OPCODE(i)) { + case OP_CALL: + case OP_TAILCALL: + case OP_RETURN: + case OP_SETLIST: { + check(GETARG_B(i) == 0); + return 1; + } + default: return 0; /* invalid instruction after an open call */ + } +} + + +static int ICACHE_FLASH_ATTR checkArgMode (const Proto *pt, int r, enum OpArgMask mode) { + switch (mode) { + case OpArgN: check(r == 0); break; + case OpArgU: break; + case OpArgR: checkreg(pt, r); break; + case OpArgK: + check(ISK(r) ? INDEXK(r) < pt->sizek : r < pt->maxstacksize); + break; + } + return 1; +} + + +static Instruction ICACHE_FLASH_ATTR symbexec (const Proto *pt, int lastpc, int reg) { + int pc; + int last; /* stores position of last instruction that changed `reg' */ + last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */ + check(precheck(pt)); + for (pc = 0; pc < lastpc; pc++) { + Instruction i = pt->code[pc]; + OpCode op = GET_OPCODE(i); + int a = GETARG_A(i); + int b = 0; + int c = 0; + check(op < NUM_OPCODES); + checkreg(pt, a); + switch (getOpMode(op)) { + case iABC: { + b = GETARG_B(i); + c = GETARG_C(i); + check(checkArgMode(pt, b, getBMode(op))); + check(checkArgMode(pt, c, getCMode(op))); + break; + } + case iABx: { + b = GETARG_Bx(i); + if (getBMode(op) == OpArgK) check(b < pt->sizek); + break; + } + case iAsBx: { + b = GETARG_sBx(i); + if (getBMode(op) == OpArgR) { + int dest = pc+1+b; + check(0 <= dest && dest < pt->sizecode); + if (dest > 0) { + int j; + /* check that it does not jump to a setlist count; this + is tricky, because the count from a previous setlist may + have the same value of an invalid setlist; so, we must + go all the way back to the first of them (if any) */ + for (j = 0; j < dest; j++) { + Instruction d = pt->code[dest-1-j]; + if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break; + } + /* if 'j' is even, previous value is not a setlist (even if + it looks like one) */ + check((j&1) == 0); + } + } + break; + } + } + if (testAMode(op)) { + if (a == reg) last = pc; /* change register `a' */ + } + if (testTMode(op)) { + check(pc+2 < pt->sizecode); /* check skip */ + check(GET_OPCODE(pt->code[pc+1]) == OP_JMP); + } + switch (op) { + case OP_LOADBOOL: { + if (c == 1) { /* does it jump? */ + check(pc+2 < pt->sizecode); /* check its jump */ + check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST || + GETARG_C(pt->code[pc+1]) != 0); + } + break; + } + case OP_LOADNIL: { + if (a <= reg && reg <= b) + last = pc; /* set registers from `a' to `b' */ + break; + } + case OP_GETUPVAL: + case OP_SETUPVAL: { + check(b < pt->nups); + break; + } + case OP_GETGLOBAL: + case OP_SETGLOBAL: { + check(ttisstring(&pt->k[b])); + break; + } + case OP_SELF: { + checkreg(pt, a+1); + if (reg == a+1) last = pc; + break; + } + case OP_CONCAT: { + check(b < c); /* at least two operands */ + break; + } + case OP_TFORLOOP: { + check(c >= 1); /* at least one result (control variable) */ + checkreg(pt, a+2+c); /* space for results */ + if (reg >= a+2) last = pc; /* affect all regs above its base */ + break; + } + case OP_FORLOOP: + case OP_FORPREP: + checkreg(pt, a+3); + /* go through */ + case OP_JMP: { + int dest = pc+1+b; + /* not full check and jump is forward and do not skip `lastpc'? */ + if (reg != NO_REG && pc < dest && dest <= lastpc) + pc += b; /* do the jump */ + break; + } + case OP_CALL: + case OP_TAILCALL: { + if (b != 0) { + checkreg(pt, a+b-1); + } + c--; /* c = num. returns */ + if (c == LUA_MULTRET) { + check(checkopenop(pt, pc)); + } + else if (c != 0) + checkreg(pt, a+c-1); + if (reg >= a) last = pc; /* affect all registers above base */ + break; + } + case OP_RETURN: { + b--; /* b = num. returns */ + if (b > 0) checkreg(pt, a+b-1); + break; + } + case OP_SETLIST: { + if (b > 0) checkreg(pt, a + b); + if (c == 0) { + pc++; + check(pc < pt->sizecode - 1); + } + break; + } + case OP_CLOSURE: { + int nup, j; + check(b < pt->sizep); + nup = pt->p[b]->nups; + check(pc + nup < pt->sizecode); + for (j = 1; j <= nup; j++) { + OpCode op1 = GET_OPCODE(pt->code[pc + j]); + check(op1 == OP_GETUPVAL || op1 == OP_MOVE); + } + if (reg != NO_REG) /* tracing? */ + pc += nup; /* do not 'execute' these pseudo-instructions */ + break; + } + case OP_VARARG: { + check((pt->is_vararg & VARARG_ISVARARG) && + !(pt->is_vararg & VARARG_NEEDSARG)); + b--; + if (b == LUA_MULTRET) check(checkopenop(pt, pc)); + checkreg(pt, a+b-1); + break; + } + default: break; + } + } + return pt->code[last]; +} + +#undef check +#undef checkjump +#undef checkreg + +/* }====================================================== */ + + +int ICACHE_FLASH_ATTR luaG_checkcode (const Proto *pt) { + return (symbexec(pt, pt->sizecode, NO_REG) != 0); +} + + +static const char *ICACHE_FLASH_ATTR kname (Proto *p, int c) { + if (ISK(c) && ttisstring(&p->k[INDEXK(c)])) + return svalue(&p->k[INDEXK(c)]); + else + return "?"; +} + + +static const char *ICACHE_FLASH_ATTR getobjname (lua_State *L, CallInfo *ci, int stackpos, + const char **name) { + if (isLua(ci)) { /* a Lua function? */ + Proto *p = ci_func(ci)->l.p; + int pc = currentpc(L, ci); + Instruction i; + *name = luaF_getlocalname(p, stackpos+1, pc); + if (*name) /* is a local? */ + return "local"; + i = symbexec(p, pc, stackpos); /* try symbolic execution */ + lua_assert(pc != -1); + switch (GET_OPCODE(i)) { + case OP_GETGLOBAL: { + int g = GETARG_Bx(i); /* global index */ + lua_assert(ttisstring(&p->k[g])); + *name = svalue(&p->k[g]); + return "global"; + } + case OP_MOVE: { + int a = GETARG_A(i); + int b = GETARG_B(i); /* move from `b' to `a' */ + if (b < a) + return getobjname(L, ci, b, name); /* get name for `b' */ + break; + } + case OP_GETTABLE: { + int k = GETARG_C(i); /* key index */ + *name = kname(p, k); + return "field"; + } + case OP_GETUPVAL: { + int u = GETARG_B(i); /* upvalue index */ + *name = p->upvalues ? getstr(p->upvalues[u]) : "?"; + return "upvalue"; + } + case OP_SELF: { + int k = GETARG_C(i); /* key index */ + *name = kname(p, k); + return "method"; + } + default: break; + } + } + return NULL; /* no useful name found */ +} + + +static const char *ICACHE_FLASH_ATTR getfuncname (lua_State *L, CallInfo *ci, const char **name) { + Instruction i; + if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1)) + return NULL; /* calling function is not Lua (or is unknown) */ + ci--; /* calling function */ + i = ci_func(ci)->l.p->code[currentpc(L, ci)]; + if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL || + GET_OPCODE(i) == OP_TFORLOOP) + return getobjname(L, ci, GETARG_A(i), name); + else + return NULL; /* no useful name can be found */ +} + + +/* only ANSI way to check whether a pointer points to an array */ +static int ICACHE_FLASH_ATTR isinstack (CallInfo *ci, const TValue *o) { + StkId p; + for (p = ci->base; p < ci->top; p++) + if (o == p) return 1; + return 0; +} + + +void ICACHE_FLASH_ATTR luaG_typeerror (lua_State *L, const TValue *o, const char *op) { + const char *name = NULL; + const char *t = luaT_typenames[ttype(o)]; + const char *kind = (isinstack(L->ci, o)) ? + getobjname(L, L->ci, cast_int(o - L->base), &name) : + NULL; + if (kind) + luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)", + op, kind, name, t); + else + luaG_runerror(L, "attempt to %s a %s value", op, t); +} + + +void ICACHE_FLASH_ATTR luaG_concaterror (lua_State *L, StkId p1, StkId p2) { + if (ttisstring(p1) || ttisnumber(p1)) p1 = p2; + lua_assert(!ttisstring(p1) && !ttisnumber(p1)); + luaG_typeerror(L, p1, "concatenate"); +} + + +void ICACHE_FLASH_ATTR luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) { + TValue temp; + if (luaV_tonumber(p1, &temp) == NULL) + p2 = p1; /* first operand is wrong */ + luaG_typeerror(L, p2, "perform arithmetic on"); +} + + +int ICACHE_FLASH_ATTR luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { + const char *t1 = luaT_typenames[ttype(p1)]; + const char *t2 = luaT_typenames[ttype(p2)]; + if (t1[2] == t2[2]) + luaG_runerror(L, "attempt to compare two %s values", t1); + else + luaG_runerror(L, "attempt to compare %s with %s", t1, t2); + return 0; +} + + +static void ICACHE_FLASH_ATTR addinfo (lua_State *L, const char *msg) { + CallInfo *ci = L->ci; + if (isLua(ci)) { /* is Lua code? */ + char buff[LUA_IDSIZE]; /* add file:line information */ + int line = currentline(L, ci); + luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE); + luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); + } +} + + +void ICACHE_FLASH_ATTR luaG_errormsg (lua_State *L) { + if (L->errfunc != 0) { /* is there an error handling function? */ + StkId errfunc = restorestack(L, L->errfunc); + if (!ttisfunction(errfunc) && !ttislightfunction(errfunc)) luaD_throw(L, LUA_ERRERR); + setobjs2s(L, L->top, L->top - 1); /* move argument */ + setobjs2s(L, L->top - 1, errfunc); /* push function */ + incr_top(L); + luaD_call(L, L->top - 2, 1); /* call it */ + } + luaD_throw(L, LUA_ERRRUN); +} + + +void ICACHE_FLASH_ATTR luaG_runerror (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + addinfo(L, luaO_pushvfstring(L, fmt, argp)); + va_end(argp); + luaG_errormsg(L); +} + diff --git a/app/lua/ldebug.h b/app/lua/ldebug.h new file mode 100644 index 00000000..ba28a972 --- /dev/null +++ b/app/lua/ldebug.h @@ -0,0 +1,33 @@ +/* +** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions from Debug Interface module +** See Copyright Notice in lua.h +*/ + +#ifndef ldebug_h +#define ldebug_h + + +#include "lstate.h" + + +#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1) + +#define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0) + +#define resethookcount(L) (L->hookcount = L->basehookcount) + + +LUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o, + const char *opname); +LUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2); +LUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...); +LUAI_FUNC void luaG_errormsg (lua_State *L); +LUAI_FUNC int luaG_checkcode (const Proto *pt); +LUAI_FUNC int luaG_checkopenop (Instruction i); + +#endif diff --git a/app/lua/ldo.c b/app/lua/ldo.c new file mode 100644 index 00000000..8f69fe56 --- /dev/null +++ b/app/lua/ldo.c @@ -0,0 +1,537 @@ +/* +** $Id: ldo.c,v 2.38.1.3 2008/01/18 22:31:22 roberto Exp $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + + +#include +#include "c_stdlib.h" +#include "c_string.h" + +#define ldo_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" +#include "lzio.h" + + + + +/* +** {====================================================== +** Error-recovery functions +** ======================================================= +*/ + + +/* chain list of long jump buffers */ +struct lua_longjmp { + struct lua_longjmp *previous; + luai_jmpbuf b; + volatile int status; /* error code */ +}; + + +void ICACHE_FLASH_ATTR luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { + switch (errcode) { + case LUA_ERRMEM: { + ptrdiff_t oldtopr = savestack(L, oldtop); + setsvalue2s(L, restorestack(L, oldtopr), luaS_newliteral(L, MEMERRMSG)); + break; + } + case LUA_ERRERR: { + ptrdiff_t oldtopr = savestack(L, oldtop); + setsvalue2s(L, restorestack(L, oldtopr), luaS_newliteral(L, "error in error handling")); + break; + } + case LUA_ERRSYNTAX: + case LUA_ERRRUN: { + setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ + break; + } + } + L->top = oldtop + 1; +} + + +static void ICACHE_FLASH_ATTR restore_stack_limit (lua_State *L) { + lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); + if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */ + int inuse = cast_int(L->ci - L->base_ci); + if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */ + luaD_reallocCI(L, LUAI_MAXCALLS); + } +} + + +static void ICACHE_FLASH_ATTR resetstack (lua_State *L, int status) { + L->ci = L->base_ci; + L->base = L->ci->base; + luaF_close(L, L->base); /* close eventual pending closures */ + luaD_seterrorobj(L, status, L->base); + L->nCcalls = L->baseCcalls; + L->allowhook = 1; + restore_stack_limit(L); + L->errfunc = 0; + L->errorJmp = NULL; +} + + +void ICACHE_FLASH_ATTR luaD_throw (lua_State *L, int errcode) { + unfixedstack(L); /* make sure the fixedstack & block_gc flags get reset. */ + unset_block_gc(L); + if (L->errorJmp) { + L->errorJmp->status = errcode; + LUAI_THROW(L, L->errorJmp); + } + else { + L->status = cast_byte(errcode); + if (G(L)->panic) { + resetstack(L, errcode); + lua_unlock(L); + G(L)->panic(L); + } + // c_exit(EXIT_FAILURE); + } +} + + +int ICACHE_FLASH_ATTR luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { + struct lua_longjmp lj; + lj.status = 0; + lj.previous = L->errorJmp; /* chain new error handler */ + L->errorJmp = &lj; + LUAI_TRY(L, &lj, + (*f)(L, ud); + ); + L->errorJmp = lj.previous; /* restore old error handler */ + return lj.status; +} + +/* }====================================================== */ + + +static void ICACHE_FLASH_ATTR correctstack (lua_State *L, TValue *oldstack) { + CallInfo *ci; + GCObject *up; + L->top = (L->top - oldstack) + L->stack; + for (up = L->openupval; up != NULL; up = up->gch.next) + gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + for (ci = L->base_ci; ci <= L->ci; ci++) { + ci->top = (ci->top - oldstack) + L->stack; + ci->base = (ci->base - oldstack) + L->stack; + ci->func = (ci->func - oldstack) + L->stack; + } + L->base = (L->base - oldstack) + L->stack; +} + + +void ICACHE_FLASH_ATTR luaD_reallocstack (lua_State *L, int newsize) { + TValue *oldstack = L->stack; + int realsize = newsize + 1 + EXTRA_STACK; + lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); + luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue); + L->stacksize = realsize; + L->stack_last = L->stack+newsize; + correctstack(L, oldstack); +} + + +void ICACHE_FLASH_ATTR luaD_reallocCI (lua_State *L, int newsize) { + CallInfo *oldci = L->base_ci; + luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo); + L->size_ci = newsize; + L->ci = (L->ci - oldci) + L->base_ci; + L->end_ci = L->base_ci + L->size_ci - 1; +} + + +void ICACHE_FLASH_ATTR luaD_growstack (lua_State *L, int n) { + if (n <= L->stacksize) /* double size is enough? */ + luaD_reallocstack(L, 2*L->stacksize); + else + luaD_reallocstack(L, L->stacksize + n); +} + + +static CallInfo *ICACHE_FLASH_ATTR growCI (lua_State *L) { + if (L->size_ci > LUAI_MAXCALLS) /* overflow while handling overflow? */ + luaD_throw(L, LUA_ERRERR); + else { + luaD_reallocCI(L, 2*L->size_ci); + if (L->size_ci > LUAI_MAXCALLS) + luaG_runerror(L, "stack overflow"); + } + return ++L->ci; +} + + +void ICACHE_FLASH_ATTR luaD_callhook (lua_State *L, int event, int line) { + lua_Hook hook = L->hook; + if (hook && L->allowhook) { + ptrdiff_t top = savestack(L, L->top); + ptrdiff_t ci_top = savestack(L, L->ci->top); + lua_Debug ar; + ar.event = event; + ar.currentline = line; + if (event == LUA_HOOKTAILRET) + ar.i_ci = 0; /* tail call; no debug information about it */ + else + ar.i_ci = cast_int(L->ci - L->base_ci); + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + L->ci->top = L->top + LUA_MINSTACK; + lua_assert(L->ci->top <= L->stack_last); + L->allowhook = 0; /* cannot call hooks inside a hook */ + lua_unlock(L); + (*hook)(L, &ar); + lua_lock(L); + lua_assert(!L->allowhook); + L->allowhook = 1; + L->ci->top = restorestack(L, ci_top); + L->top = restorestack(L, top); + } +} + + +static StkId ICACHE_FLASH_ATTR adjust_varargs (lua_State *L, Proto *p, int actual) { + int i; + int nfixargs = p->numparams; +#if defined(LUA_COMPAT_VARARG) + Table *htab = NULL; +#endif + StkId base, fixed; + for (; actual < nfixargs; ++actual) + setnilvalue(L->top++); +#if defined(LUA_COMPAT_VARARG) + if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */ + int nvar = actual - nfixargs; /* number of extra arguments */ + lua_assert(p->is_vararg & VARARG_HASARG); + luaC_checkGC(L); + htab = luaH_new(L, nvar, 1); /* create `arg' table */ + sethvalue2s(L, L->top, htab); /* put table on stack */ + incr_top(L); + fixedstack(L); + for (i=0; itop - 1 - nvar + i); + unfixedstack(L); + /* store counter in field `n' */ + setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, "n")), cast_num(nvar)); + L->top--; /* remove table from stack */ + } +#endif + /* move fixed parameters to final position */ + fixed = L->top - actual; /* first fixed argument */ + base = L->top; /* final position of first argument */ + for (i=0; itop++, fixed+i); + setnilvalue(fixed+i); + } +#if defined(LUA_COMPAT_VARARG) + /* add `arg' parameter */ + if (htab) { + sethvalue(L, L->top++, htab); + lua_assert(iswhite(obj2gco(htab))); + } +#endif + return base; +} + + +static StkId ICACHE_FLASH_ATTR tryfuncTM (lua_State *L, StkId func) { + const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL); + StkId p; + ptrdiff_t funcr = savestack(L, func); + if (!ttisfunction(tm)) + luaG_typeerror(L, func, "call"); + /* Open a hole inside the stack at `func' */ + for (p = L->top; p > func; p--) setobjs2s(L, p, p-1); + incr_top(L); + func = restorestack(L, funcr); /* previous call may change stack */ + setobj2s(L, func, tm); /* tag method is the new function to be called */ + return func; +} + + + +#define inc_ci(L) \ + ((L->ci == L->end_ci) ? growCI(L) : \ + (condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci)) + + +int ICACHE_FLASH_ATTR luaD_precall (lua_State *L, StkId func, int nresults) { + ptrdiff_t funcr; + LClosure *cl = NULL; + if (!ttisfunction(func) && !ttislightfunction(func)) /* `func' is not a function? */ + func = tryfuncTM(L, func); /* check the `function' tag method */ + funcr = savestack(L, func); + if (ttisfunction(func)) + cl = &clvalue(func)->l; + L->ci->savedpc = L->savedpc; + if (cl && !cl->isC) { /* Lua function? prepare its call */ + CallInfo *ci; + StkId st, base; + Proto *p = cl->p; + luaD_checkstack(L, p->maxstacksize); + func = restorestack(L, funcr); + if (!p->is_vararg) { /* no varargs? */ + base = func + 1; + if (L->top > base + p->numparams) + L->top = base + p->numparams; + } + else { /* vararg function */ + int nargs = cast_int(L->top - func) - 1; + base = adjust_varargs(L, p, nargs); + func = restorestack(L, funcr); /* previous call may change the stack */ + } + ci = inc_ci(L); /* now `enter' new function */ + ci->func = func; + L->base = ci->base = base; + ci->top = L->base + p->maxstacksize; + lua_assert(ci->top <= L->stack_last); + L->savedpc = p->code; /* starting point */ + ci->tailcalls = 0; + ci->nresults = nresults; + for (st = L->top; st < ci->top; st++) + setnilvalue(st); + L->top = ci->top; + if (L->hookmask & LUA_MASKCALL) { + L->savedpc++; /* hooks assume 'pc' is already incremented */ + luaD_callhook(L, LUA_HOOKCALL, -1); + L->savedpc--; /* correct 'pc' */ + } + return PCRLUA; + } + else { /* if is a C function, call it */ + CallInfo *ci; + int n; + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + ci = inc_ci(L); /* now `enter' new function */ + ci->func = restorestack(L, funcr); + L->base = ci->base = ci->func + 1; + ci->top = L->top + LUA_MINSTACK; + lua_assert(ci->top <= L->stack_last); + ci->nresults = nresults; + if (L->hookmask & LUA_MASKCALL) + luaD_callhook(L, LUA_HOOKCALL, -1); + lua_unlock(L); + if (ttisfunction(ci->func)) + n = (*curr_func(L)->c.f)(L); /* do the actual call */ + else + n = ((lua_CFunction)fvalue(ci->func))(L); /* do the actual call */ + lua_lock(L); + if (n < 0) /* yielding? */ + return PCRYIELD; + else { + luaD_poscall(L, L->top - n); + return PCRC; + } + } +} + + +static StkId ICACHE_FLASH_ATTR callrethooks (lua_State *L, StkId firstResult) { + ptrdiff_t fr = savestack(L, firstResult); /* next call may change stack */ + luaD_callhook(L, LUA_HOOKRET, -1); + if (f_isLua(L->ci)) { /* Lua function? */ + while ((L->hookmask & LUA_MASKRET) && L->ci->tailcalls--) /* tail calls */ + luaD_callhook(L, LUA_HOOKTAILRET, -1); + } + return restorestack(L, fr); +} + + +int ICACHE_FLASH_ATTR luaD_poscall (lua_State *L, StkId firstResult) { + StkId res; + int wanted, i; + CallInfo *ci; + if (L->hookmask & LUA_MASKRET) + firstResult = callrethooks(L, firstResult); + ci = L->ci--; + res = ci->func; /* res == final position of 1st result */ + wanted = ci->nresults; + L->base = (ci - 1)->base; /* restore base */ + L->savedpc = (ci - 1)->savedpc; /* restore savedpc */ + /* move results to correct place */ + for (i = wanted; i != 0 && firstResult < L->top; i--) + setobjs2s(L, res++, firstResult++); + while (i-- > 0) + setnilvalue(res++); + L->top = res; + return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */ +} + + +/* +** Call a function (C or Lua). The function to be called is at *func. +** The arguments are on the stack, right after the function. +** When returns, all the results are on the stack, starting at the original +** function position. +*/ +void ICACHE_FLASH_ATTR luaD_call (lua_State *L, StkId func, int nResults) { + if (++L->nCcalls >= LUAI_MAXCCALLS) { + if (L->nCcalls == LUAI_MAXCCALLS) + luaG_runerror(L, "C stack overflow"); + else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) + luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ + } + if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */ + luaV_execute(L, 1); /* call it */ + L->nCcalls--; + luaC_checkGC(L); +} + + +static void ICACHE_FLASH_ATTR resume (lua_State *L, void *ud) { + StkId firstArg = cast(StkId, ud); + CallInfo *ci = L->ci; + if (L->status == 0) { /* start coroutine? */ + lua_assert(ci == L->base_ci && firstArg > L->base); + if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA) + return; + } + else { /* resuming from previous yield */ + lua_assert(L->status == LUA_YIELD); + L->status = 0; + if (!f_isLua(ci)) { /* `common' yield? */ + /* finish interrupted execution of `OP_CALL' */ + lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL || + GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL); + if (luaD_poscall(L, firstArg)) /* complete it... */ + L->top = L->ci->top; /* and correct top if not multiple results */ + } + else /* yielded inside a hook: just continue its execution */ + L->base = L->ci->base; + } + luaV_execute(L, cast_int(L->ci - L->base_ci)); +} + + +static int ICACHE_FLASH_ATTR resume_error (lua_State *L, const char *msg) { + L->top = L->ci->base; + setsvalue2s(L, L->top, luaS_new(L, msg)); + incr_top(L); + lua_unlock(L); + return LUA_ERRRUN; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_resume (lua_State *L, int nargs) { + int status; + lua_lock(L); + if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci)) + return resume_error(L, "cannot resume non-suspended coroutine"); + if (L->nCcalls >= LUAI_MAXCCALLS) + return resume_error(L, "C stack overflow"); + luai_userstateresume(L, nargs); + lua_assert(L->errfunc == 0); + L->baseCcalls = ++L->nCcalls; + status = luaD_rawrunprotected(L, resume, L->top - nargs); + if (status != 0) { /* error? */ + L->status = cast_byte(status); /* mark thread as `dead' */ + luaD_seterrorobj(L, status, L->top); + L->ci->top = L->top; + } + else { + lua_assert(L->nCcalls == L->baseCcalls); + status = L->status; + } + --L->nCcalls; + lua_unlock(L); + return status; +} + + +LUA_API int ICACHE_FLASH_ATTR lua_yield (lua_State *L, int nresults) { + luai_userstateyield(L, nresults); + lua_lock(L); + if (L->nCcalls > L->baseCcalls) + luaG_runerror(L, "attempt to yield across metamethod/C-call boundary"); + L->base = L->top - nresults; /* protect stack slots below */ + L->status = LUA_YIELD; + lua_unlock(L); + return -1; +} + + +int ICACHE_FLASH_ATTR luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t old_top, ptrdiff_t ef) { + int status; + unsigned short oldnCcalls = L->nCcalls; + ptrdiff_t old_ci = saveci(L, L->ci); + lu_byte old_allowhooks = L->allowhook; + ptrdiff_t old_errfunc = L->errfunc; + L->errfunc = ef; + status = luaD_rawrunprotected(L, func, u); + if (status != 0) { /* an error occurred? */ + StkId oldtop = restorestack(L, old_top); + luaF_close(L, oldtop); /* close eventual pending closures */ + luaD_seterrorobj(L, status, oldtop); + L->nCcalls = oldnCcalls; + L->ci = restoreci(L, old_ci); + L->base = L->ci->base; + L->savedpc = L->ci->savedpc; + L->allowhook = old_allowhooks; + restore_stack_limit(L); + } + L->errfunc = old_errfunc; + return status; +} + + + +/* +** Execute a protected parser. +*/ +struct SParser { /* data to `f_parser' */ + ZIO *z; + Mbuffer buff; /* buffer to be used by the scanner */ + const char *name; +}; + +static void ICACHE_FLASH_ATTR f_parser (lua_State *L, void *ud) { + int i; + Proto *tf; + Closure *cl; + struct SParser *p = cast(struct SParser *, ud); + int c = luaZ_lookahead(p->z); + luaC_checkGC(L); + set_block_gc(L); /* stop collector during parsing */ + tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z, + &p->buff, p->name); + cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L))); + cl->l.p = tf; + for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */ + cl->l.upvals[i] = luaF_newupval(L); + setclvalue(L, L->top, cl); + incr_top(L); + unset_block_gc(L); +} + + +int ICACHE_FLASH_ATTR luaD_protectedparser (lua_State *L, ZIO *z, const char *name) { + struct SParser p; + int status; + p.z = z; p.name = name; + luaZ_initbuffer(L, &p.buff); + status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); + luaZ_freebuffer(L, &p.buff); + return status; +} + + diff --git a/app/lua/ldo.h b/app/lua/ldo.h new file mode 100644 index 00000000..98fddac5 --- /dev/null +++ b/app/lua/ldo.h @@ -0,0 +1,57 @@ +/* +** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + +#ifndef ldo_h +#define ldo_h + + +#include "lobject.h" +#include "lstate.h" +#include "lzio.h" + + +#define luaD_checkstack(L,n) \ + if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \ + luaD_growstack(L, n); \ + else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); + + +#define incr_top(L) {luaD_checkstack(L,1); L->top++;} + +#define savestack(L,p) ((char *)(p) - (char *)L->stack) +#define restorestack(L,n) ((TValue *)((char *)L->stack + (n))) + +#define saveci(L,p) ((char *)(p) - (char *)L->base_ci) +#define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n))) + + +/* results from luaD_precall */ +#define PCRLUA 0 /* initiated a call to a Lua function */ +#define PCRC 1 /* did a call to a C function */ +#define PCRYIELD 2 /* C funtion yielded */ + + +/* type of protected functions, to be ran by `runprotected' */ +typedef void (*Pfunc) (lua_State *L, void *ud); + +LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name); +LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line); +LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); +LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); +LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t oldtop, ptrdiff_t ef); +LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult); +LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize); +LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize); +LUAI_FUNC void luaD_growstack (lua_State *L, int n); + +LUAI_FUNC void luaD_throw (lua_State *L, int errcode); +LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); + +LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); + +#endif + diff --git a/app/lua/ldump.c b/app/lua/ldump.c new file mode 100644 index 00000000..d8e34044 --- /dev/null +++ b/app/lua/ldump.c @@ -0,0 +1,320 @@ +/* +** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ +** save precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#include "c_stddef.h" +#include "c_string.h" + +#define ldump_c +#define LUA_CORE + +#include "lua.h" + +#include "lobject.h" +#include "lstate.h" +#include "lundump.h" + +typedef struct { + lua_State* L; + lua_Writer writer; + void* data; + int strip; + int status; + DumpTargetInfo target; + size_t wrote; +} DumpState; + +#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D) +#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D) + +static void ICACHE_FLASH_ATTR DumpBlock(const void* b, size_t size, DumpState* D) +{ + if (D->status==0) + { + lua_unlock(D->L); + D->status=(*D->writer)(D->L,b,size,D->data); + D->wrote+=size; + lua_lock(D->L); + } +} + +static void ICACHE_FLASH_ATTR DumpChar(int y, DumpState* D) +{ + char x=(char)y; + DumpVar(x,D); +} + +static void ICACHE_FLASH_ATTR Align4(DumpState *D) +{ + while(D->wrote&3) + DumpChar(0,D); +} + +static void ICACHE_FLASH_ATTR MaybeByteSwap(char *number, size_t numbersize, DumpState *D) +{ + int x=1; + int platform_little_endian = *(char*)&x; + if (platform_little_endian != D->target.little_endian) + { + unsigned long i; + for (i=0; i0x7F || x<(-0x80)) D->status=LUA_ERR_CC_INTOVERFLOW; + DumpChar(x,D); + } break; + case 2: { + if (x>0x7FFF || x<(-0x8000)) D->status=LUA_ERR_CC_INTOVERFLOW; + int16_t y=(int16_t)x; + MaybeByteSwap((char*)&y,2,D); + DumpVar(y,D); + } break; + case 4: { + /* Need to reduce bounds by 1 to avoid messing 32-bit compilers up */ + if (x>0x7FFFFFFE || x<(-0x7FFFFFFF)) D->status=LUA_ERR_CC_INTOVERFLOW; + int32_t y=(int32_t)x; + MaybeByteSwap((char*)&y,4,D); + DumpVar(y,D); + } break; + default: lua_assert(0); + } +} + +static void ICACHE_FLASH_ATTR DumpInt(int x, DumpState* D) +{ + DumpIntWithSize(x,D->target.sizeof_int,D); +} + +static void ICACHE_FLASH_ATTR DumpSize(uint32_t x, DumpState* D) +{ + /* dump unsigned integer */ + switch(D->target.sizeof_strsize_t) { + case 1: { + if (x>0xFF) D->status=LUA_ERR_CC_INTOVERFLOW; + DumpChar(x,D); + } break; + case 2: { + if (x>0xFFFF) D->status=LUA_ERR_CC_INTOVERFLOW; + uint16_t y=(uint16_t)x; + MaybeByteSwap((char*)&y,2,D); + DumpVar(y,D); + } break; + case 4: { + /* Reduce bounds to avoid messing 32-bit compilers up */ + if (x>0xFFFFFFFE) D->status=LUA_ERR_CC_INTOVERFLOW; + uint32_t y=x; + MaybeByteSwap((char*)&y,4,D); + DumpVar(y,D); + } break; + default: lua_assert(0); + } +} + +static void ICACHE_FLASH_ATTR DumpNumber(lua_Number x, DumpState* D) +{ +#if defined( LUA_NUMBER_INTEGRAL ) && !defined( LUA_CROSS_COMPILER ) + DumpIntWithSize(x,D->target.sizeof_lua_Number,D); +#else // #if defined( LUA_NUMBER_INTEGRAL ) && !defined( LUA_CROSS_COMPILER ) + if (D->target.lua_Number_integral) + { + if (((float)(int)x)!=x) D->status=LUA_ERR_CC_NOTINTEGER; + DumpIntWithSize(x,D->target.sizeof_lua_Number,D); + } + else + { + switch(D->target.sizeof_lua_Number) + { + /* do we need bounds checking? */ + case 4: { + float y=x; + MaybeByteSwap((char*)&y,4,D); + DumpVar(y,D); + } break; + case 8: { + double y=x; + // ARM FPA mode: keep endianness, but swap high and low parts of the + // memory representation. This is the default compilation mode for ARM + // targets with non-EABI gcc + if(D->target.is_arm_fpa) + { + char *pnum=(char*)&y, temp[4]; + c_memcpy(temp,pnum,4); + c_memcpy(pnum,pnum+4,4); + c_memcpy(pnum+4,temp,4); + } + MaybeByteSwap((char*)&y,8,D); + DumpVar(y,D); + } break; + default: lua_assert(0); + } + } +#endif // #if defined( LUA_NUMBER_INTEGRAL ) && !defined( LUA_CROSS_COMPILER ) +} + +static void ICACHE_FLASH_ATTR DumpCode(const Proto *f, DumpState* D) +{ + DumpInt(f->sizecode,D); + char buf[10]; + int i; + Align4(D); + for (i=0; isizecode; i++) + { + c_memcpy(buf,&f->code[i],sizeof(Instruction)); + MaybeByteSwap(buf,sizeof(Instruction),D); + DumpBlock(buf,sizeof(Instruction),D); + } +} + +static void ICACHE_FLASH_ATTR DumpString(const TString* s, DumpState* D) +{ + if (s==NULL || getstr(s)==NULL) + { + strsize_t size=0; + DumpSize(size,D); + } + else + { + strsize_t size=( strsize_t )s->tsv.len+1; /* include trailing '\0' */ + DumpSize(size,D); + DumpBlock(getstr(s),size,D); + } +} + +static void DumpFunction(const Proto* f, const TString* p, DumpState* D); + +static void ICACHE_FLASH_ATTR DumpConstants(const Proto* f, DumpState* D) +{ + int i,n=f->sizek; + DumpInt(n,D); + for (i=0; ik[i]; + DumpChar(ttype(o),D); + switch (ttype(o)) + { + case LUA_TNIL: + break; + case LUA_TBOOLEAN: + DumpChar(bvalue(o),D); + break; + case LUA_TNUMBER: + DumpNumber(nvalue(o),D); + break; + case LUA_TSTRING: + DumpString(rawtsvalue(o),D); + break; + default: + lua_assert(0); /* cannot happen */ + break; + } + } + n=f->sizep; + DumpInt(n,D); + for (i=0; ip[i],f->source,D); +} + +static void ICACHE_FLASH_ATTR DumpDebug(const Proto* f, DumpState* D) +{ + int i,n; + n= (D->strip) ? 0 : f->sizelineinfo; + DumpInt(n,D); + Align4(D); + for (i=0; ilineinfo[i],D); + } + + n= (D->strip) ? 0 : f->sizelocvars; + DumpInt(n,D); + for (i=0; ilocvars[i].varname,D); + DumpInt(f->locvars[i].startpc,D); + DumpInt(f->locvars[i].endpc,D); + } + + n= (D->strip) ? 0 : f->sizeupvalues; + DumpInt(n,D); + for (i=0; iupvalues[i],D); +} + +static void ICACHE_FLASH_ATTR DumpFunction(const Proto* f, const TString* p, DumpState* D) +{ + DumpString((f->source==p || D->strip) ? NULL : f->source,D); + DumpInt(f->linedefined,D); + DumpInt(f->lastlinedefined,D); + DumpChar(f->nups,D); + DumpChar(f->numparams,D); + DumpChar(f->is_vararg,D); + DumpChar(f->maxstacksize,D); + DumpCode(f,D); + DumpConstants(f,D); + DumpDebug(f,D); +} + +static void ICACHE_FLASH_ATTR DumpHeader(DumpState* D) +{ + char buf[LUAC_HEADERSIZE]; + char *h=buf; + + /* This code must be kept in sync wiht luaU_header */ + c_memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1); + h+=sizeof(LUA_SIGNATURE)-1; + *h++=(char)LUAC_VERSION; + *h++=(char)LUAC_FORMAT; + *h++=(char)D->target.little_endian; + *h++=(char)D->target.sizeof_int; + *h++=(char)D->target.sizeof_strsize_t; + *h++=(char)sizeof(Instruction); + *h++=(char)D->target.sizeof_lua_Number; + *h++=(char)D->target.lua_Number_integral; + + DumpBlock(buf,LUAC_HEADERSIZE,D); +} + +/* +** dump Lua function as precompiled chunk with specified target +*/ +int ICACHE_FLASH_ATTR luaU_dump_crosscompile (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip, DumpTargetInfo target) +{ + DumpState D; + D.L=L; + D.writer=w; + D.data=data; + D.strip=strip; + D.status=0; + D.target=target; + D.wrote=0; + DumpHeader(&D); + DumpFunction(f,NULL,&D); + return D.status; +} + +/* + ** dump Lua function as precompiled chunk with local machine as target + */ +int ICACHE_FLASH_ATTR luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip) +{ + DumpTargetInfo target; + int test=1; + target.little_endian=*(char*)&test; + target.sizeof_int=sizeof(int); + target.sizeof_strsize_t=sizeof(strsize_t); + target.sizeof_lua_Number=sizeof(lua_Number); + target.lua_Number_integral=(((lua_Number)0.5)==0); + target.is_arm_fpa=0; + return luaU_dump_crosscompile(L,f,w,data,strip,target); +} diff --git a/app/lua/legc.c b/app/lua/legc.c new file mode 100644 index 00000000..9dc7c050 --- /dev/null +++ b/app/lua/legc.c @@ -0,0 +1,13 @@ +// Lua EGC (Emergeny Garbage Collector) interface + +#include "legc.h" +#include "lstate.h" +#include "c_types.h" + +void ICACHE_FLASH_ATTR legc_set_mode(lua_State *L, int mode, unsigned limit) { + global_State *g = G(L); + + g->egcmode = mode; + g->memlimit = limit; +} + diff --git a/app/lua/legc.h b/app/lua/legc.h new file mode 100644 index 00000000..a0d9dde3 --- /dev/null +++ b/app/lua/legc.h @@ -0,0 +1,17 @@ +// Lua EGC (Emergeny Garbage Collector) interface + +#ifndef __LEGC_H__ +#define __LEGC_H__ + +#include "lstate.h" + +// EGC operations modes +#define EGC_NOT_ACTIVE 0 // EGC disabled +#define EGC_ON_ALLOC_FAILURE 1 // run EGC on allocation failure +#define EGC_ON_MEM_LIMIT 2 // run EGC when an upper memory limit is hit +#define EGC_ALWAYS 4 // always run EGC before an allocation + +void legc_set_mode(lua_State *L, int mode, unsigned limit); + +#endif + diff --git a/app/lua/lfunc.c b/app/lua/lfunc.c new file mode 100644 index 00000000..48f0db5f --- /dev/null +++ b/app/lua/lfunc.c @@ -0,0 +1,176 @@ +/* +** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + + +#include "c_stddef.h" + +#define lfunc_c +#define LUA_CORE + +#include "lua.h" + +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +Closure *ICACHE_FLASH_ATTR luaF_newCclosure (lua_State *L, int nelems, Table *e) { + Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems))); + luaC_link(L, obj2gco(c), LUA_TFUNCTION); + c->c.isC = 1; + c->c.env = e; + c->c.nupvalues = cast_byte(nelems); + return c; +} + + +Closure *ICACHE_FLASH_ATTR luaF_newLclosure (lua_State *L, int nelems, Table *e) { + Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems))); + luaC_link(L, obj2gco(c), LUA_TFUNCTION); + c->l.isC = 0; + c->l.env = e; + c->l.nupvalues = cast_byte(nelems); + while (nelems--) c->l.upvals[nelems] = NULL; + return c; +} + + +UpVal *ICACHE_FLASH_ATTR luaF_newupval (lua_State *L) { + UpVal *uv = luaM_new(L, UpVal); + luaC_link(L, obj2gco(uv), LUA_TUPVAL); + uv->v = &uv->u.value; + setnilvalue(uv->v); + return uv; +} + + +UpVal *ICACHE_FLASH_ATTR luaF_findupval (lua_State *L, StkId level) { + global_State *g = G(L); + GCObject **pp = &L->openupval; + UpVal *p; + UpVal *uv; + while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) { + lua_assert(p->v != &p->u.value); + if (p->v == level) { /* found a corresponding upvalue? */ + if (isdead(g, obj2gco(p))) /* is it dead? */ + changewhite(obj2gco(p)); /* ressurect it */ + return p; + } + pp = &p->next; + } + uv = luaM_new(L, UpVal); /* not found: create a new one */ + uv->tt = LUA_TUPVAL; + uv->v = level; /* current value lives in the stack */ + uv->next = *pp; /* chain it in the proper position */ + *pp = obj2gco(uv); + uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */ + uv->u.l.next = g->uvhead.u.l.next; + uv->u.l.next->u.l.prev = uv; + g->uvhead.u.l.next = uv; + luaC_marknew(L, obj2gco(uv)); + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + return uv; +} + + +static void ICACHE_FLASH_ATTR unlinkupval (UpVal *uv) { + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */ + uv->u.l.prev->u.l.next = uv->u.l.next; +} + + +void ICACHE_FLASH_ATTR luaF_freeupval (lua_State *L, UpVal *uv) { + if (uv->v != &uv->u.value) /* is it open? */ + unlinkupval(uv); /* remove from open list */ + luaM_free(L, uv); /* free upvalue */ +} + + +void ICACHE_FLASH_ATTR luaF_close (lua_State *L, StkId level) { + UpVal *uv; + global_State *g = G(L); + while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) { + GCObject *o = obj2gco(uv); + lua_assert(!isblack(o) && uv->v != &uv->u.value); + L->openupval = uv->next; /* remove from `open' list */ + if (isdead(g, o)) + luaF_freeupval(L, uv); /* free upvalue */ + else { + unlinkupval(uv); + setobj(L, &uv->u.value, uv->v); + uv->v = &uv->u.value; /* now current value lives here */ + luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */ + } + } +} + + +Proto *ICACHE_FLASH_ATTR luaF_newproto (lua_State *L) { + Proto *f = luaM_new(L, Proto); + luaC_link(L, obj2gco(f), LUA_TPROTO); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->sizelineinfo = 0; + f->sizeupvalues = 0; + f->nups = 0; + f->upvalues = NULL; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->lineinfo = NULL; + f->sizelocvars = 0; + f->locvars = NULL; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + + +void ICACHE_FLASH_ATTR luaF_freeproto (lua_State *L, Proto *f) { + luaM_freearray(L, f->p, f->sizep, Proto *); + luaM_freearray(L, f->k, f->sizek, TValue); + luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar); + luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *); + if (!proto_is_readonly(f)) { + luaM_freearray(L, f->code, f->sizecode, Instruction); + luaM_freearray(L, f->lineinfo, f->sizelineinfo, int); + } + luaM_free(L, f); +} + + +void ICACHE_FLASH_ATTR luaF_freeclosure (lua_State *L, Closure *c) { + int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) : + sizeLclosure(c->l.nupvalues); + luaM_freemem(L, c, size); +} + + +/* +** Look for n-th local variable at line `line' in function `func'. +** Returns NULL if not found. +*/ +const char *ICACHE_FLASH_ATTR luaF_getlocalname (const Proto *f, int local_number, int pc) { + int i; + for (i = 0; isizelocvars && f->locvars[i].startpc <= pc; i++) { + if (pc < f->locvars[i].endpc) { /* is variable active? */ + local_number--; + if (local_number == 0) + return getstr(f->locvars[i].varname); + } + } + return NULL; /* not found */ +} + diff --git a/app/lua/lfunc.h b/app/lua/lfunc.h new file mode 100644 index 00000000..1450bb7d --- /dev/null +++ b/app/lua/lfunc.h @@ -0,0 +1,37 @@ +/* +** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + +#ifndef lfunc_h +#define lfunc_h + + +#include "lobject.h" + +#include "lgc.h" + +#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \ + cast(int, sizeof(TValue)*((n)-1))) + +#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \ + cast(int, sizeof(TValue *)*((n)-1))) + +#define proto_readonly(p) l_setbit((p)->marked, READONLYBIT) +#define proto_is_readonly(p) testbit((p)->marked, READONLYBIT) + +LUAI_FUNC Proto *luaF_newproto (lua_State *L); +LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e); +LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e); +LUAI_FUNC UpVal *luaF_newupval (lua_State *L); +LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); +LUAI_FUNC void luaF_close (lua_State *L, StkId level); +LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); +LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c); +LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv); +LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, + int pc); + + +#endif diff --git a/app/lua/lgc.c b/app/lua/lgc.c new file mode 100644 index 00000000..77beaa31 --- /dev/null +++ b/app/lua/lgc.c @@ -0,0 +1,743 @@ +/* +** $Id: lgc.c,v 2.38.1.1 2007/12/27 13:02:25 roberto Exp $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#include "c_string.h" + +#define lgc_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lrotable.h" + +#define GCSTEPSIZE 1024u +#define GCSWEEPMAX 40 +#define GCSWEEPCOST 10 +#define GCFINALIZECOST 100 + + +#define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS)) + +#define makewhite(g,x) \ + ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g))) + +#define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) +#define black2gray(x) resetbit((x)->gch.marked, BLACKBIT) + +#define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT) + + +#define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) +#define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT) + + +#define KEYWEAK bitmask(KEYWEAKBIT) +#define VALUEWEAK bitmask(VALUEWEAKBIT) + + + +#define markvalue(g,o) { checkconsistency(o); \ + if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); } + +#define markobject(g,t) { if (iswhite(obj2gco(t))) \ + reallymarkobject(g, obj2gco(t)); } + + +#define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) + + +static void ICACHE_FLASH_ATTR removeentry (Node *n) { + lua_assert(ttisnil(gval(n))); + if (iscollectable(gkey(n))) + setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */ +} + + +static void ICACHE_FLASH_ATTR reallymarkobject (global_State *g, GCObject *o) { + lua_assert(iswhite(o) && !isdead(g, o)); + white2gray(o); + switch (o->gch.tt) { + case LUA_TSTRING: { + return; + } + case LUA_TUSERDATA: { + Table *mt = gco2u(o)->metatable; + gray2black(o); /* udata are never gray */ + if (mt && !luaR_isrotable(mt)) markobject(g, mt); + markobject(g, gco2u(o)->env); + return; + } + case LUA_TUPVAL: { + UpVal *uv = gco2uv(o); + markvalue(g, uv->v); + if (uv->v == &uv->u.value) /* closed? */ + gray2black(o); /* open upvalues are never black */ + return; + } + case LUA_TFUNCTION: { + gco2cl(o)->c.gclist = g->gray; + g->gray = o; + break; + } + case LUA_TTABLE: { + gco2h(o)->gclist = g->gray; + g->gray = o; + break; + } + case LUA_TTHREAD: { + gco2th(o)->gclist = g->gray; + g->gray = o; + break; + } + case LUA_TPROTO: { + gco2p(o)->gclist = g->gray; + g->gray = o; + break; + } + default: lua_assert(0); + } +} + + +static void ICACHE_FLASH_ATTR marktmu (global_State *g) { + GCObject *u = g->tmudata; + if (u) { + do { + u = u->gch.next; + makewhite(g, u); /* may be marked, if left from previous GC */ + reallymarkobject(g, u); + } while (u != g->tmudata); + } +} + + +/* move `dead' udata that need finalization to list `tmudata' */ +size_t ICACHE_FLASH_ATTR luaC_separateudata (lua_State *L, int all) { + global_State *g = G(L); + size_t deadmem = 0; + GCObject **p = &g->mainthread->next; + GCObject *curr; + while ((curr = *p) != NULL) { + if (!(iswhite(curr) || all) || isfinalized(gco2u(curr))) + p = &curr->gch.next; /* don't bother with them */ + else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) { + markfinalized(gco2u(curr)); /* don't need finalization */ + p = &curr->gch.next; + } + else { /* must call its gc method */ + deadmem += sizeudata(gco2u(curr)); + markfinalized(gco2u(curr)); + *p = curr->gch.next; + /* link `curr' at the end of `tmudata' list */ + if (g->tmudata == NULL) /* list is empty? */ + g->tmudata = curr->gch.next = curr; /* creates a circular list */ + else { + curr->gch.next = g->tmudata->gch.next; + g->tmudata->gch.next = curr; + g->tmudata = curr; + } + } + } + return deadmem; +} + + +static int ICACHE_FLASH_ATTR traversetable (global_State *g, Table *h) { + int i; + int weakkey = 0; + int weakvalue = 0; + const TValue *mode; + if (h->metatable && !luaR_isrotable(h->metatable)) + markobject(g, h->metatable); + mode = gfasttm(g, h->metatable, TM_MODE); + if (mode && ttisstring(mode)) { /* is there a weak mode? */ + weakkey = (c_strchr(svalue(mode), 'k') != NULL); + weakvalue = (c_strchr(svalue(mode), 'v') != NULL); + if (weakkey || weakvalue) { /* is really weak? */ + h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ + h->marked |= cast_byte((weakkey << KEYWEAKBIT) | + (weakvalue << VALUEWEAKBIT)); + h->gclist = g->weak; /* must be cleared after GC, ... */ + g->weak = obj2gco(h); /* ... so put in the appropriate list */ + } + } + if (weakkey && weakvalue) return 1; + if (!weakvalue) { + i = h->sizearray; + while (i--) + markvalue(g, &h->array[i]); + } + i = sizenode(h); + while (i--) { + Node *n = gnode(h, i); + lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); + if (ttisnil(gval(n))) + removeentry(n); /* remove empty entries */ + else { + lua_assert(!ttisnil(gkey(n))); + if (!weakkey) markvalue(g, gkey(n)); + if (!weakvalue) markvalue(g, gval(n)); + } + } + return weakkey || weakvalue; +} + + +/* +** All marks are conditional because a GC may happen while the +** prototype is still being created +*/ +static void ICACHE_FLASH_ATTR traverseproto (global_State *g, Proto *f) { + int i; + if (f->source) stringmark(f->source); + for (i=0; isizek; i++) /* mark literals */ + markvalue(g, &f->k[i]); + for (i=0; isizeupvalues; i++) { /* mark upvalue names */ + if (f->upvalues[i]) + stringmark(f->upvalues[i]); + } + for (i=0; isizep; i++) { /* mark nested protos */ + if (f->p[i]) + markobject(g, f->p[i]); + } + for (i=0; isizelocvars; i++) { /* mark local-variable names */ + if (f->locvars[i].varname) + stringmark(f->locvars[i].varname); + } +} + + + +static void ICACHE_FLASH_ATTR traverseclosure (global_State *g, Closure *cl) { + markobject(g, cl->c.env); + if (cl->c.isC) { + int i; + for (i=0; ic.nupvalues; i++) /* mark its upvalues */ + markvalue(g, &cl->c.upvalue[i]); + } + else { + int i; + lua_assert(cl->l.nupvalues == cl->l.p->nups); + markobject(g, cl->l.p); + for (i=0; il.nupvalues; i++) { /* mark its upvalues */ + if(cl->l.upvals[i]) + markobject(g, cl->l.upvals[i]); + } + } +} + + +static void ICACHE_FLASH_ATTR checkstacksizes (lua_State *L, StkId max) { + int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */ + int s_used = cast_int(max - L->stack); /* part of stack in use */ + if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */ + return; /* do not touch the stacks */ + if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci) + luaD_reallocCI(L, L->size_ci/2); /* still big enough... */ + condhardstacktests(luaD_reallocCI(L, ci_used + 1)); + if (4*s_used < L->stacksize && + 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize) + luaD_reallocstack(L, L->stacksize/2); /* still big enough... */ + condhardstacktests(luaD_reallocstack(L, s_used)); +} + + +static void ICACHE_FLASH_ATTR traversestack (global_State *g, lua_State *l) { + StkId o, lim; + CallInfo *ci; + markvalue(g, gt(l)); + lim = l->top; + if(l->stack == NULL) return; /* no stack to traverse */ + for (ci = l->base_ci; ci <= l->ci; ci++) { + lua_assert(ci->top <= l->stack_last); + if (lim < ci->top) lim = ci->top; + } + for (o = l->stack; o < l->top; o++) + markvalue(g, o); + for (; o <= lim; o++) + setnilvalue(o); + if (!isfixedstack(l)) /* if stack size is fixed, can't resize it. */ + checkstacksizes(l, lim); +} + + +/* +** traverse one gray object, turning it to black. +** Returns `quantity' traversed. +*/ +static l_mem ICACHE_FLASH_ATTR propagatemark (global_State *g) { + GCObject *o = g->gray; + lua_assert(isgray(o)); + gray2black(o); + switch (o->gch.tt) { + case LUA_TTABLE: { + Table *h = gco2h(o); + g->gray = h->gclist; + if (traversetable(g, h)) /* table is weak? */ + black2gray(o); /* keep it gray */ + return sizeof(Table) + sizeof(TValue) * h->sizearray + + sizeof(Node) * sizenode(h); + } + case LUA_TFUNCTION: { + Closure *cl = gco2cl(o); + g->gray = cl->c.gclist; + traverseclosure(g, cl); + return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) : + sizeLclosure(cl->l.nupvalues); + } + case LUA_TTHREAD: { + lua_State *th = gco2th(o); + g->gray = th->gclist; + th->gclist = g->grayagain; + g->grayagain = o; + black2gray(o); + traversestack(g, th); + return sizeof(lua_State) + sizeof(TValue) * th->stacksize + + sizeof(CallInfo) * th->size_ci; + } + case LUA_TPROTO: { + Proto *p = gco2p(o); + g->gray = p->gclist; + traverseproto(g, p); + return sizeof(Proto) + sizeof(Proto *) * p->sizep + + sizeof(TValue) * p->sizek + + sizeof(LocVar) * p->sizelocvars + + sizeof(TString *) * p->sizeupvalues + + (proto_is_readonly(p) ? 0 : sizeof(Instruction) * p->sizecode + + sizeof(int) * p->sizelineinfo); + } + default: lua_assert(0); return 0; + } +} + + +static size_t ICACHE_FLASH_ATTR propagateall (global_State *g) { + size_t m = 0; + while (g->gray) m += propagatemark(g); + return m; +} + + +/* +** The next function tells whether a key or value can be cleared from +** a weak table. Non-collectable objects are never removed from weak +** tables. Strings behave as `values', so are never removed too. for +** other objects: if really collected, cannot keep them; for userdata +** being finalized, keep them in keys, but not in values +*/ +static int ICACHE_FLASH_ATTR iscleared (const TValue *o, int iskey) { + if (!iscollectable(o)) return 0; + if (ttisstring(o)) { + stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ + return 0; + } + return iswhite(gcvalue(o)) || + (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); +} + + +/* +** clear collected entries from weaktables +*/ +static void ICACHE_FLASH_ATTR cleartable (GCObject *l) { + while (l) { + Table *h = gco2h(l); + int i = h->sizearray; + lua_assert(testbit(h->marked, VALUEWEAKBIT) || + testbit(h->marked, KEYWEAKBIT)); + if (testbit(h->marked, VALUEWEAKBIT)) { + while (i--) { + TValue *o = &h->array[i]; + if (iscleared(o, 0)) /* value was collected? */ + setnilvalue(o); /* remove value */ + } + } + i = sizenode(h); + while (i--) { + Node *n = gnode(h, i); + if (!ttisnil(gval(n)) && /* non-empty entry? */ + (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) { + setnilvalue(gval(n)); /* remove value ... */ + removeentry(n); /* remove entry from table */ + } + } + l = h->gclist; + } +} + + +static void ICACHE_FLASH_ATTR freeobj (lua_State *L, GCObject *o) { + switch (o->gch.tt) { + case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; + case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break; + case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; + case LUA_TTABLE: luaH_free(L, gco2h(o)); break; + case LUA_TTHREAD: { + lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread); + luaE_freethread(L, gco2th(o)); + break; + } + case LUA_TSTRING: { + G(L)->strt.nuse--; + luaM_freemem(L, o, sizestring(gco2ts(o))); + break; + } + case LUA_TUSERDATA: { + luaM_freemem(L, o, sizeudata(gco2u(o))); + break; + } + default: lua_assert(0); + } +} + + + +#define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) + + +static GCObject **ICACHE_FLASH_ATTR sweeplist (lua_State *L, GCObject **p, lu_mem count) { + GCObject *curr; + global_State *g = G(L); + int deadmask = otherwhite(g); + while ((curr = *p) != NULL && count-- > 0) { + if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */ + sweepwholelist(L, &gco2th(curr)->openupval); + if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */ + lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); + makewhite(g, curr); /* make it white (for next cycle) */ + p = &curr->gch.next; + } + else { /* must erase `curr' */ + lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); + *p = curr->gch.next; + freeobj(L, curr); + } + } + return p; +} + + +static void ICACHE_FLASH_ATTR checkSizes (lua_State *L) { + global_State *g = G(L); + /* check size of string hash */ + if (g->strt.nuse < cast(lu_int32, g->strt.size/4) && + g->strt.size > MINSTRTABSIZE*2) + luaS_resize(L, g->strt.size/2); /* table is too big */ + /* it is not safe to re-size the buffer if it is in use. */ + if (luaZ_bufflen(&g->buff) > 0) return; + /* check size of buffer */ + if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */ + size_t newsize = luaZ_sizebuffer(&g->buff) / 2; + luaZ_resizebuffer(L, &g->buff, newsize); + } +} + + +static void ICACHE_FLASH_ATTR GCTM (lua_State *L) { + global_State *g = G(L); + GCObject *o = g->tmudata->gch.next; /* get first element */ + Udata *udata = rawgco2u(o); + const TValue *tm; + /* remove udata from `tmudata' */ + if (o == g->tmudata) /* last element? */ + g->tmudata = NULL; + else + g->tmudata->gch.next = udata->uv.next; + udata->uv.next = g->mainthread->next; /* return it to `root' list */ + g->mainthread->next = o; + makewhite(g, o); + tm = fasttm(L, udata->uv.metatable, TM_GC); + if (tm != NULL) { + lu_byte oldah = L->allowhook; + lu_mem oldt = g->GCthreshold; + L->allowhook = 0; /* stop debug hooks during GC tag method */ + g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */ + setobj2s(L, L->top, tm); + setuvalue(L, L->top+1, udata); + L->top += 2; + luaD_call(L, L->top - 2, 0); + L->allowhook = oldah; /* restore hooks */ + g->GCthreshold = oldt; /* restore threshold */ + } +} + + +/* +** Call all GC tag methods +*/ +void ICACHE_FLASH_ATTR luaC_callGCTM (lua_State *L) { + while (G(L)->tmudata) + GCTM(L); +} + + +void ICACHE_FLASH_ATTR luaC_freeall (lua_State *L) { + global_State *g = G(L); + int i; + g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */ + sweepwholelist(L, &g->rootgc); + for (i = 0; i < g->strt.size; i++) /* free all string lists */ + sweepwholelist(L, &g->strt.hash[i]); +} + + +static void ICACHE_FLASH_ATTR markmt (global_State *g) { + int i; + for (i=0; imt[i] && !luaR_isrotable(g->mt[i])) markobject(g, g->mt[i]); +} + + +/* mark root set */ +static void ICACHE_FLASH_ATTR markroot (lua_State *L) { + global_State *g = G(L); + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + markobject(g, g->mainthread); + /* make global table be traversed before main stack */ + markvalue(g, gt(g->mainthread)); + markvalue(g, registry(L)); + markmt(g); + g->gcstate = GCSpropagate; +} + + +static void ICACHE_FLASH_ATTR remarkupvals (global_State *g) { + UpVal *uv; + for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + if (isgray(obj2gco(uv))) + markvalue(g, uv->v); + } +} + + +static void ICACHE_FLASH_ATTR atomic (lua_State *L) { + global_State *g = G(L); + size_t udsize; /* total size of userdata to be finalized */ + /* remark occasional upvalues of (maybe) dead threads */ + remarkupvals(g); + /* traverse objects cautch by write barrier and by 'remarkupvals' */ + propagateall(g); + /* remark weak tables */ + g->gray = g->weak; + g->weak = NULL; + lua_assert(!iswhite(obj2gco(g->mainthread))); + markobject(g, L); /* mark running thread */ + markmt(g); /* mark basic metatables (again) */ + propagateall(g); + /* remark gray again */ + g->gray = g->grayagain; + g->grayagain = NULL; + propagateall(g); + udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ + marktmu(g); /* mark `preserved' userdata */ + udsize += propagateall(g); /* remark, to propagate `preserveness' */ + cleartable(g->weak); /* remove collected objects from weak tables */ + /* flip current white */ + g->currentwhite = cast_byte(otherwhite(g)); + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + g->gcstate = GCSsweepstring; + g->estimate = g->totalbytes - udsize; /* first estimate */ +} + +static void ICACHE_FLASH_ATTR sweepstrstep (global_State *g, lua_State *L) { + lu_mem old = g->totalbytes; + sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); + if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ + g->gcstate = GCSsweep; /* end sweep-string phase */ + lua_assert(old >= g->totalbytes); + g->estimate -= old - g->totalbytes; +} + + +static l_mem ICACHE_FLASH_ATTR singlestep (lua_State *L) { + global_State *g = G(L); + /*lua_checkmemory(L);*/ + switch (g->gcstate) { + case GCSpause: { + markroot(L); /* start a new collection */ + return 0; + } + case GCSpropagate: { + if (g->gray) + return propagatemark(g); + else { /* no more `gray' objects */ + atomic(L); /* finish mark phase */ + return 0; + } + } + case GCSsweepstring: { + sweepstrstep(g, L); + return GCSWEEPCOST; + } + case GCSsweep: { + lu_mem old = g->totalbytes; + g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); + if (*g->sweepgc == NULL) { /* nothing more to sweep? */ + checkSizes(L); + g->gcstate = GCSfinalize; /* end sweep phase */ + } + lua_assert(old >= g->totalbytes); + g->estimate -= old - g->totalbytes; + return GCSWEEPMAX*GCSWEEPCOST; + } + case GCSfinalize: { + if (g->tmudata) { + GCTM(L); + if (g->estimate > GCFINALIZECOST) + g->estimate -= GCFINALIZECOST; + return GCFINALIZECOST; + } + else { + g->gcstate = GCSpause; /* end collection */ + g->gcdept = 0; + return 0; + } + } + default: lua_assert(0); return 0; + } +} + + +void ICACHE_FLASH_ATTR luaC_step (lua_State *L) { + global_State *g = G(L); + if(is_block_gc(L)) return; + set_block_gc(L); + l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; + if (lim == 0) + lim = (MAX_LUMEM-1)/2; /* no limit */ + g->gcdept += g->totalbytes - g->GCthreshold; + if (g->estimate > g->totalbytes) + g->estimate = g->totalbytes; + do { + lim -= singlestep(L); + if (g->gcstate == GCSpause) + break; + } while (lim > 0); + if (g->gcstate != GCSpause) { + if (g->gcdept < GCSTEPSIZE) + g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/ + else { + g->gcdept -= GCSTEPSIZE; + g->GCthreshold = g->totalbytes; + } + } + else { + lua_assert(g->totalbytes >= g->estimate); + setthreshold(g); + } + unset_block_gc(L); +} + +int ICACHE_FLASH_ATTR luaC_sweepstrgc (lua_State *L) { + global_State *g = G(L); + if (g->gcstate == GCSsweepstring) { + sweepstrstep(g, L); + return (g->gcstate == GCSsweepstring) ? 1 : 0; + } + return 0; +} + +void ICACHE_FLASH_ATTR luaC_fullgc (lua_State *L) { + global_State *g = G(L); + if(is_block_gc(L)) return; + set_block_gc(L); + if (g->gcstate <= GCSpropagate) { + /* reset sweep marks to sweep all elements (returning them to white) */ + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + /* reset other collector lists */ + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + g->gcstate = GCSsweepstring; + } + lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); + /* finish any pending sweep phase */ + while (g->gcstate != GCSfinalize) { + lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep); + singlestep(L); + } + markroot(L); + while (g->gcstate != GCSpause) { + singlestep(L); + } + setthreshold(g); + unset_block_gc(L); +} + + +void ICACHE_FLASH_ATTR luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { + global_State *g = G(L); + lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + lua_assert(ttype(&o->gch) != LUA_TTABLE); + /* must keep invariant? */ + if (g->gcstate == GCSpropagate) + reallymarkobject(g, v); /* restore invariant */ + else /* don't mind */ + makewhite(g, o); /* mark as white just to avoid other barriers */ +} + + +void ICACHE_FLASH_ATTR luaC_barrierback (lua_State *L, Table *t) { + global_State *g = G(L); + GCObject *o = obj2gco(t); + lua_assert(isblack(o) && !isdead(g, o)); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + black2gray(o); /* make table gray (again) */ + t->gclist = g->grayagain; + g->grayagain = o; +} + + +void ICACHE_FLASH_ATTR luaC_marknew (lua_State *L, GCObject *o) { + global_State *g = G(L); + o->gch.marked = luaC_white(g); + if (g->gcstate == GCSpropagate) + reallymarkobject(g, o); /* mark new objects as gray during propagate state. */ +} + + +void ICACHE_FLASH_ATTR luaC_link (lua_State *L, GCObject *o, lu_byte tt) { + global_State *g = G(L); + o->gch.next = g->rootgc; + g->rootgc = o; + o->gch.marked = luaC_white(g); + o->gch.tt = tt; +} + + +void ICACHE_FLASH_ATTR luaC_linkupval (lua_State *L, UpVal *uv) { + global_State *g = G(L); + GCObject *o = obj2gco(uv); + o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */ + g->rootgc = o; + if (isgray(o)) { + if (g->gcstate == GCSpropagate) { + gray2black(o); /* closed upvalues need barrier */ + luaC_barrier(L, uv, uv->v); + } + else { /* sweep phase: sweep it (turning it into white) */ + makewhite(g, o); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + } + } +} + diff --git a/app/lua/lgc.h b/app/lua/lgc.h new file mode 100644 index 00000000..9c26932b --- /dev/null +++ b/app/lua/lgc.h @@ -0,0 +1,136 @@ +/* +** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#ifndef lgc_h +#define lgc_h + + +#include "lobject.h" + + +/* +** Possible states of the Garbage Collector +*/ +#define GCSpause 0 +#define GCSpropagate 1 +#define GCSsweepstring 2 +#define GCSsweep 3 +#define GCSfinalize 4 + + +/* +** some userful bit tricks +*/ +#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m))) +#define setbits(x,m) ((x) |= (m)) +#define testbits(x,m) ((x) & (m)) +#define bitmask(b) (1<<(b)) +#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) +#define l_setbit(x,b) setbits(x, bitmask(b)) +#define resetbit(x,b) resetbits(x, bitmask(b)) +#define testbit(x,b) testbits(x, bitmask(b)) +#define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2))) +#define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2))) +#define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2))) + + +/* +** Possible Garbage Collector flags. +** Layout for bit use in 'gsflags' field in global_State structure. +** bit 0 - Protect GC from recursive calls. +** bit 1 - Don't try to shrink string table if EGC was called during a string table resize. +*/ +#define GCFlagsNone 0 +#define GCBlockGCBit 0 +#define GCResizingStringsBit 1 + + +#define is_block_gc(L) testbit(G(L)->gcflags, GCBlockGCBit) +#define set_block_gc(L) l_setbit(G(L)->gcflags, GCBlockGCBit) +#define unset_block_gc(L) resetbit(G(L)->gcflags, GCBlockGCBit) +#define is_resizing_strings_gc(L) testbit(G(L)->gcflags, GCResizingStringsBit) +#define set_resizing_strings_gc(L) l_setbit(G(L)->gcflags, GCResizingStringsBit) +#define unset_resizing_strings_gc(L) resetbit(G(L)->gcflags, GCResizingStringsBit) + +/* +** Layout for bit use in `marked' field: +** bit 0 - object is white (type 0) +** bit 1 - object is white (type 1) +** bit 2 - object is black +** bit 3 - for thread: Don't resize thread's stack +** bit 3 - for userdata: has been finalized +** bit 3 - for tables: has weak keys +** bit 4 - for tables: has weak values +** bit 5 - object is fixed (should not be collected) +** bit 6 - object is "super" fixed (only the main thread) +** bit 7 - object is (partially) stored in read-only memory +*/ + + +#define WHITE0BIT 0 +#define WHITE1BIT 1 +#define BLACKBIT 2 +#define FIXEDSTACKBIT 3 +#define FINALIZEDBIT 3 +#define KEYWEAKBIT 3 +#define VALUEWEAKBIT 4 +#define FIXEDBIT 5 +#define SFIXEDBIT 6 +#define READONLYBIT 7 +#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) + + +#define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) +#define isblack(x) testbit((x)->gch.marked, BLACKBIT) +#define isgray(x) (!isblack(x) && !iswhite(x)) + +#define otherwhite(g) (g->currentwhite ^ WHITEBITS) +#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS) + +#define changewhite(x) ((x)->gch.marked ^= WHITEBITS) +#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT) + +#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) + +#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) + +#define isfixedstack(x) testbit((x)->marked, FIXEDSTACKBIT) +#define fixedstack(x) l_setbit((x)->marked, FIXEDSTACKBIT) +#define unfixedstack(x) resetbit((x)->marked, FIXEDSTACKBIT) + +#define luaC_checkGC(L) { \ + condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \ + if (G(L)->totalbytes >= G(L)->GCthreshold) \ + luaC_step(L); } + + +#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ + luaC_barrierf(L,obj2gco(p),gcvalue(v)); } + +#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \ + luaC_barrierback(L,t); } + +#define luaC_objbarrier(L,p,o) \ + { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \ + luaC_barrierf(L,obj2gco(p),obj2gco(o)); } + +#define luaC_objbarriert(L,t,o) \ + { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); } + +LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all); +LUAI_FUNC void luaC_callGCTM (lua_State *L); +LUAI_FUNC void luaC_freeall (lua_State *L); +LUAI_FUNC void luaC_step (lua_State *L); +LUAI_FUNC void luaC_fullgc (lua_State *L); +LUAI_FUNC int luaC_sweepstrgc (lua_State *L); +LUAI_FUNC void luaC_marknew (lua_State *L, GCObject *o); +LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt); +LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv); +LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v); +LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t); + + +#endif diff --git a/app/lua/linit.c b/app/lua/linit.c new file mode 100644 index 00000000..2d85cad7 --- /dev/null +++ b/app/lua/linit.c @@ -0,0 +1,82 @@ +/* +** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $ +** Initialization of libraries for lua.c +** See Copyright Notice in lua.h +*/ + + +#define linit_c +#define LUA_LIB + +#include "lua.h" + +#include "lualib.h" +#include "lauxlib.h" +#include "lrotable.h" +#include "luaconf.h" + +#if defined(LUA_USE_MODULES) +#include "modules.h" +#endif + +#if defined(LUA_MODULES_ROM) +#undef _ROM +#define _ROM( name, openf, table ) extern int openf(lua_State *); +LUA_MODULES_ROM +#endif + +static const luaL_Reg lualibs[] = { + {"", luaopen_base}, + {LUA_LOADLIBNAME, luaopen_package}, + // {LUA_IOLIBNAME, luaopen_io}, + {LUA_STRLIBNAME, luaopen_string}, +#if LUA_OPTIMIZE_MEMORY == 0 + // {LUA_MATHLIBNAME, luaopen_math}, + {LUA_TABLIBNAME, luaopen_table}, + // {LUA_DBLIBNAME, luaopen_debug}, +#endif +#if defined(LUA_MODULES_ROM) +#undef _ROM +#define _ROM( name, openf, table ) { name, openf }, + LUA_MODULES_ROM +#endif + {NULL, NULL} +}; + +extern const luaR_entry strlib[]; +extern const luaR_entry syslib[]; +extern const luaR_entry tab_funcs[]; +// extern const luaR_entry dblib[]; +extern const luaR_entry co_funcs[]; +// extern const luaR_entry math_map[]; +#if defined(LUA_MODULES_ROM) && LUA_OPTIMIZE_MEMORY == 2 +#undef _ROM +#define _ROM( name, openf, table ) extern const luaR_entry table[]; +LUA_MODULES_ROM +#endif +const luaR_table lua_rotable[] = +{ +#if LUA_OPTIMIZE_MEMORY > 0 + {LUA_STRLIBNAME, strlib}, + {LUA_TABLIBNAME, tab_funcs}, + // {LUA_DBLIBNAME, dblib}, + {LUA_COLIBNAME, co_funcs}, + // {LUA_MATHLIBNAME, math_map}, +#if defined(LUA_MODULES_ROM) && LUA_OPTIMIZE_MEMORY == 2 +#undef _ROM +#define _ROM( name, openf, table ) { name, table }, + LUA_MODULES_ROM +#endif +#endif + {NULL, NULL} +}; + +LUALIB_API void ICACHE_FLASH_ATTR luaL_openlibs (lua_State *L) { + const luaL_Reg *lib = lualibs; + for (; lib->func; lib++) { + lua_pushcfunction(L, lib->func); + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); + } +} + diff --git a/app/lua/liolib.c b/app/lua/liolib.c new file mode 100644 index 00000000..5334bb32 --- /dev/null +++ b/app/lua/liolib.c @@ -0,0 +1,659 @@ +/* +** $Id: liolib.c,v 2.73.1.4 2010/05/14 15:33:51 roberto Exp $ +** Standard I/O (and system) library +** See Copyright Notice in lua.h +*/ + + +// #include "c_errno.h" +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_string.h" +#include "flash_fs.h" + +#define liolib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "lrotable.h" + + +#define IO_INPUT 1 +#define IO_OUTPUT 2 +#define IO_STDERR 0 + +#if LUA_OPTIMIZE_MEMORY != 2 +#define LUA_IO_GETFIELD(f) lua_rawgeti(L, LUA_ENVIRONINDEX, f) +#define LUA_IO_SETFIELD(f) lua_rawseti(L, LUA_ENVIRONINDEX, f) +#else +#define LUA_IO_GETFIELD(f) lua_rawgeti(L, LUA_REGISTRYINDEX, liolib_keys[f]) +#define LUA_IO_SETFIELD(f) lua_rawseti(L, LUA_REGISTRYINDEX, liolib_keys[f]) + +/* "Pseudo-random" keys for the registry */ +static const int liolib_keys[] = {(int)&luaL_callmeta, (int)&luaL_typerror, (int)&luaL_argerror}; +#endif + +static const char *const fnames[] = {"input", "output"}; + +static int ICACHE_FLASH_ATTR pushresult (lua_State *L, int i, const char *filename) { + int en = fs_error(0); /* calls to Lua API may change this value */ + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + if (filename) + lua_pushfstring(L, "%s: err(%d)", filename, en); + else + lua_pushfstring(L, "err(%d)", en); + lua_pushinteger(L, en); + return 3; + } +} + + +static void ICACHE_FLASH_ATTR fileerror (lua_State *L, int arg, const char *filename) { + lua_pushfstring(L, "%s: err(%d)", filename, fs_error(0)); + luaL_argerror(L, arg, lua_tostring(L, -1)); +} + + +#define tofilep(L) ((int *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) + + +static int ICACHE_FLASH_ATTR io_type (lua_State *L) { + void *ud; + luaL_checkany(L, 1); + ud = lua_touserdata(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); + if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1)) + lua_pushnil(L); /* not a file */ + else if (*((int *)ud) < FS_OPEN_OK) + lua_pushliteral(L, "closed file"); + else + lua_pushliteral(L, "file"); + return 1; +} + + +static int ICACHE_FLASH_ATTR tofile (lua_State *L) { + int *f = tofilep(L); + if (*f < FS_OPEN_OK) + luaL_error(L, "attempt to use a closed file"); + return *f; +} + + + +/* +** When creating file handles, always creates a `closed' file handle +** before opening the actual file; so, if there is a memory error, the +** file is not left opened. +*/ +static int *ICACHE_FLASH_ATTR newfile (lua_State *L) { + int *pf = (int *)lua_newuserdata(L, sizeof(int)); + *pf = FS_OPEN_OK - 1; /* file handle is currently `closed' */ + luaL_getmetatable(L, LUA_FILEHANDLE); + lua_setmetatable(L, -2); + return pf; +} + + +#if LUA_OPTIMIZE_MEMORY != 2 +/* +** function to (not) close the standard files stdin, stdout, and stderr +*/ +static int ICACHE_FLASH_ATTR io_noclose (lua_State *L) { + lua_pushnil(L); + lua_pushliteral(L, "cannot close standard file"); + return 2; +} + +#if 0 +/* +** function to close 'popen' files +*/ +static int ICACHE_FLASH_ATTR io_pclose (lua_State *L) { + int *p = tofilep(L); + int ok = lua_pclose(L, *p); + *p = FS_OPEN_OK - 1; + return pushresult(L, ok, NULL); +} +#endif + +/* +** function to close regular files +*/ +static int ICACHE_FLASH_ATTR io_fclose (lua_State *L) { + int *p = tofilep(L); + int ok = (fs_close(*p) == 0); + *p = FS_OPEN_OK - 1; + return pushresult(L, ok, NULL); +} +#endif + +static int ICACHE_FLASH_ATTR aux_close (lua_State *L) { +#if LUA_OPTIMIZE_MEMORY != 2 + lua_getfenv(L, 1); + lua_getfield(L, -1, "__close"); + return (lua_tocfunction(L, -1))(L); +#else + int *p = tofilep(L); + if(*p == c_stdin || *p == c_stdout || *p == c_stderr) + { + lua_pushnil(L); + lua_pushliteral(L, "cannot close standard file"); + return 2; + } + int ok = (fs_close(*p) == 0); + *p = FS_OPEN_OK - 1; + return pushresult(L, ok, NULL); +#endif +} + + +static int ICACHE_FLASH_ATTR io_close (lua_State *L) { + if (lua_isnone(L, 1)) + LUA_IO_GETFIELD(IO_OUTPUT); + tofile(L); /* make sure argument is a file */ + return aux_close(L); +} + + +static int ICACHE_FLASH_ATTR io_gc (lua_State *L) { + int f = *tofilep(L); + /* ignore closed files */ + if (f != FS_OPEN_OK - 1) + aux_close(L); + return 0; +} + + +static int ICACHE_FLASH_ATTR io_tostring (lua_State *L) { + int f = *tofilep(L); + if (f == FS_OPEN_OK - 1) + lua_pushliteral(L, "file (closed)"); + else + lua_pushfstring(L, "file (%i)", f); + return 1; +} + + +static int ICACHE_FLASH_ATTR io_open (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + int *pf = newfile(L); + *pf = fs_open(filename, fs_mode2flag(mode)); + return (*pf == FS_OPEN_OK - 1) ? pushresult(L, 0, filename) : 1; +} + + +/* +** this function has a separated environment, which defines the +** correct __close for 'popen' files +*/ +#if 0 +static int ICACHE_FLASH_ATTR io_popen (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + int *pf = newfile(L); + *pf = lua_popen(L, filename, fs_mode2flag(mode)); + return (*pf == FS_OPEN_OK - 1) ? pushresult(L, 0, filename) : 1; +} + + +static int ICACHE_FLASH_ATTR io_tmpfile (lua_State *L) { + int *pf = newfile(L); + *pf = tmpfile(); + return (*pf == FS_OPEN_OK - 1) ? pushresult(L, 0, NULL) : 1; +} +#endif + +static int ICACHE_FLASH_ATTR getiofile (lua_State *L, int findex) { + int *pf; + LUA_IO_GETFIELD(findex); + pf = (int *)lua_touserdata(L, -1); + if (pf == NULL || *pf == FS_OPEN_OK - 1){ + luaL_error(L, "default %s file is closed", fnames[findex - 1]); + return FS_OPEN_OK - 1; + } + return *pf; +} + + +static int ICACHE_FLASH_ATTR g_iofile (lua_State *L, int f, const char *mode) { + if (!lua_isnoneornil(L, 1)) { + const char *filename = lua_tostring(L, 1); + if (filename) { + int *pf = newfile(L); + *pf = fs_open(filename, fs_mode2flag(mode)); + if (*pf == FS_OPEN_OK - 1) + fileerror(L, 1, filename); + } + else { + tofile(L); /* check that it's a valid file handle */ + lua_pushvalue(L, 1); + } + LUA_IO_SETFIELD(f); + } + /* return current value */ + LUA_IO_GETFIELD(f); + return 1; +} + + +static int ICACHE_FLASH_ATTR io_input (lua_State *L) { + return g_iofile(L, IO_INPUT, "r"); +} + + +static int ICACHE_FLASH_ATTR io_output (lua_State *L) { + return g_iofile(L, IO_OUTPUT, "w"); +} + + +static int ICACHE_FLASH_ATTR io_readline (lua_State *L); + + +static void ICACHE_FLASH_ATTR aux_lines (lua_State *L, int idx, int toclose) { + lua_pushvalue(L, idx); + lua_pushboolean(L, toclose); /* close/not close file when finished */ + lua_pushcclosure(L, io_readline, 2); +} + + +static int ICACHE_FLASH_ATTR f_lines (lua_State *L) { + tofile(L); /* check that it's a valid file handle */ + aux_lines(L, 1, 0); + return 1; +} + + +static int ICACHE_FLASH_ATTR io_lines (lua_State *L) { + if (lua_isnoneornil(L, 1)) { /* no arguments? */ + /* will iterate over default input */ + LUA_IO_GETFIELD(IO_INPUT); + return f_lines(L); + } + else { + const char *filename = luaL_checkstring(L, 1); + int *pf = newfile(L); + *pf = fs_open(filename, FS_RDONLY); + if (*pf == FS_OPEN_OK - 1) + fileerror(L, 1, filename); + aux_lines(L, lua_gettop(L), 1); + return 1; + } +} + + +/* +** {====================================================== +** READ +** ======================================================= +*/ + +#if 0 +static int ICACHE_FLASH_ATTR read_number (lua_State *L, int f) { + lua_Number d; + if (fs_scanf(f, LUA_NUMBER_SCAN, &d) == 1) { + lua_pushnumber(L, d); + return 1; + } + else { + lua_pushnil(L); /* "result" to be removed */ + return 0; /* read fails */ + } +} +#endif + +static int ICACHE_FLASH_ATTR test_eof (lua_State *L, int f) { + int c = fs_getc(f); + fs_ungetc(c, f); + lua_pushlstring(L, NULL, 0); + return (c != EOF); +} + +#if 0 +static int ICACHE_FLASH_ATTR read_line (lua_State *L, int f) { + luaL_Buffer b; + luaL_buffinit(L, &b); + for (;;) { + size_t l; + char *p = luaL_prepbuffer(&b); + if (fs_gets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ + luaL_pushresult(&b); /* close buffer */ + return (lua_objlen(L, -1) > 0); /* check whether read something */ + } + l = c_strlen(p); + if (l == 0 || p[l-1] != '\n') + luaL_addsize(&b, l); + else { + luaL_addsize(&b, l - 1); /* do not include `eol' */ + luaL_pushresult(&b); /* close buffer */ + return 1; /* read at least an `eol' */ + } + } +} +#else +static int ICACHE_FLASH_ATTR read_line (lua_State *L, int f) { + luaL_Buffer b; + luaL_buffinit(L, &b); + char *p = luaL_prepbuffer(&b); + signed char c = EOF; + int i = 0; + do{ + c = (signed char)fs_getc(f); + if(c==EOF){ + break; + } + p[i++] = c; + }while((c!=EOF) && (c!='\n') && (i0 && p[i-1] == '\n') + i--; /* do not include `eol' */ + + if(i==0){ + luaL_pushresult(&b); /* close buffer */ + return (lua_objlen(L, -1) > 0); /* check whether read something */ + } + + luaL_addsize(&b, i); + luaL_pushresult(&b); /* close buffer */ + return 1; /* read at least an `eol' */ +} +#endif + +static int ICACHE_FLASH_ATTR read_chars (lua_State *L, int f, size_t n) { + size_t rlen; /* how much to read */ + size_t nr; /* number of chars actually read */ + luaL_Buffer b; + luaL_buffinit(L, &b); + rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ + do { + char *p = luaL_prepbuffer(&b); + if (rlen > n) rlen = n; /* cannot read more than asked */ + nr = fs_read(f, p, rlen); + luaL_addsize(&b, nr); + n -= nr; /* still have to read `n' chars */ + } while (n > 0 && nr == rlen); /* until end of count or eof */ + luaL_pushresult(&b); /* close buffer */ + return (n == 0 || lua_objlen(L, -1) > 0); +} + + +static int ICACHE_FLASH_ATTR g_read (lua_State *L, int f, int first) { + int nargs = lua_gettop(L) - 1; + int success; + int n; + fs_clearerr(f); + if (nargs == 0) { /* no arguments? */ + success = read_line(L, f); + n = first+1; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = first; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tointeger(L, n); + success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { +#if 0 + case 'n': /* number */ + success = read_number(L, f); + break; +#endif + case 'l': /* line */ + success = read_line(L, f); + break; + case 'a': /* file */ + read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (fs_error(f)) + return pushresult(L, 0, NULL); + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - first; +} + + +static int ICACHE_FLASH_ATTR io_read (lua_State *L) { + return g_read(L, getiofile(L, IO_INPUT), 1); +} + + +static int ICACHE_FLASH_ATTR f_read (lua_State *L) { + return g_read(L, tofile(L), 2); +} + + +static int ICACHE_FLASH_ATTR io_readline (lua_State *L) { + int *pf = (int *)lua_touserdata(L, lua_upvalueindex(1)); + int sucess; + if (pf == NULL || *pf == FS_OPEN_OK - 1){ /* file is already closed? */ + luaL_error(L, "file is already closed"); + return 0; + } + sucess = read_line(L, *pf); + if (fs_error(*pf)) + return luaL_error(L, "err(%d)", fs_error(*pf)); + if (sucess) return 1; + else { /* EOF */ + if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */ + lua_settop(L, 0); + lua_pushvalue(L, lua_upvalueindex(1)); + aux_close(L); /* close it */ + } + return 0; + } +} + +/* }====================================================== */ + + +static int ICACHE_FLASH_ATTR g_write (lua_State *L, int f, int arg) { + int nargs = lua_gettop(L) - 1; + int status = 1; + for (; nargs--; arg++) { +#if 0 + if (lua_type(L, arg) == LUA_TNUMBER) { + /* optimization: could be done exactly as for strings */ + status = status && + fs_printf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; + } + else +#endif + { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + status = status && (fs_write(f, s, l) == l); + } + } + return pushresult(L, status, NULL); +} + + +static int ICACHE_FLASH_ATTR io_write (lua_State *L) { + return g_write(L, getiofile(L, IO_OUTPUT), 1); +} + + +static int ICACHE_FLASH_ATTR f_write (lua_State *L) { + return g_write(L, tofile(L), 2); +} + + +static int ICACHE_FLASH_ATTR f_seek (lua_State *L) { + static const int mode[] = {FS_SEEK_SET, FS_SEEK_CUR, FS_SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + int f = tofile(L); + int op = luaL_checkoption(L, 2, "cur", modenames); + long offset = luaL_optlong(L, 3, 0); + op = fs_seek(f, offset, mode[op]); + if (op) + return pushresult(L, 0, NULL); /* error */ + else { + lua_pushinteger(L, fs_tell(f)); + return 1; + } +} + +#if 0 +static int ICACHE_FLASH_ATTR f_setvbuf (lua_State *L) { + static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; + static const char *const modenames[] = {"no", "full", "line", NULL}; + int f = tofile(L); + int op = luaL_checkoption(L, 2, NULL, modenames); + lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); + int res = setvbuf(f, NULL, mode[op], sz); + return pushresult(L, res == 0, NULL); +} +#endif + + +static int ICACHE_FLASH_ATTR io_flush (lua_State *L) { + return pushresult(L, fs_flush(getiofile(L, IO_OUTPUT)) == 0, NULL); +} + + +static int ICACHE_FLASH_ATTR f_flush (lua_State *L) { + return pushresult(L, fs_flush(tofile(L)) == 0, NULL); +} + +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +#if LUA_OPTIMIZE_MEMORY == 2 +const LUA_REG_TYPE iolib_funcs[] = { +#else +const LUA_REG_TYPE iolib[] = { +#endif + {LSTRKEY("close"), LFUNCVAL(io_close)}, + {LSTRKEY("flush"), LFUNCVAL(io_flush)}, + {LSTRKEY("input"), LFUNCVAL(io_input)}, + {LSTRKEY("lines"), LFUNCVAL(io_lines)}, + {LSTRKEY("open"), LFUNCVAL(io_open)}, + {LSTRKEY("output"), LFUNCVAL(io_output)}, + // {LSTRKEY("popen"), LFUNCVAL(io_popen)}, + {LSTRKEY("read"), LFUNCVAL(io_read)}, + // {LSTRKEY("tmpfile"), LFUNCVAL(io_tmpfile)}, + {LSTRKEY("type"), LFUNCVAL(io_type)}, + {LSTRKEY("write"), LFUNCVAL(io_write)}, + {LNILKEY, LNILVAL} +}; + +#if LUA_OPTIMIZE_MEMORY == 2 +static int ICACHE_FLASH_ATTR luaL_index(lua_State *L) +{ + return luaR_findfunction(L, iolib_funcs); +} + +const luaL_Reg iolib[] = { + {"__index", luaL_index}, + {NULL, NULL} +}; +#endif + +#undef MIN_OPT_LEVEL +#define MIN_OPT_LEVEL 1 +#include "lrodefs.h" +const LUA_REG_TYPE flib[] = { + {LSTRKEY("close"), LFUNCVAL(io_close)}, + {LSTRKEY("flush"), LFUNCVAL(f_flush)}, + {LSTRKEY("lines"), LFUNCVAL(f_lines)}, + {LSTRKEY("read"), LFUNCVAL(f_read)}, + {LSTRKEY("seek"), LFUNCVAL(f_seek)}, + // {LSTRKEY("setvbuf"), LFUNCVAL(f_setvbuf)}, + {LSTRKEY("write"), LFUNCVAL(f_write)}, + {LSTRKEY("__gc"), LFUNCVAL(io_gc)}, + {LSTRKEY("__tostring"), LFUNCVAL(io_tostring)}, +#if LUA_OPTIMIZE_MEMORY > 0 + {LSTRKEY("__index"), LROVAL(flib)}, +#endif + {LNILKEY, LNILVAL} +}; + +static void ICACHE_FLASH_ATTR createmeta (lua_State *L) { +#if LUA_OPTIMIZE_MEMORY == 0 + luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ + lua_pushvalue(L, -1); /* push metatable */ + lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ + luaL_register(L, NULL, flib); /* file methods */ +#else + luaL_rometatable(L, LUA_FILEHANDLE, (void*)flib); /* create metatable for file handles */ +#endif +} + + +static void ICACHE_FLASH_ATTR createstdfile (lua_State *L, int f, int k, const char *fname) { + *newfile(L) = f; +#if LUA_OPTIMIZE_MEMORY != 2 + if (k > 0) { + lua_pushvalue(L, -1); + lua_rawseti(L, LUA_ENVIRONINDEX, k); + } + lua_pushvalue(L, -2); /* copy environment */ + lua_setfenv(L, -2); /* set it */ + lua_setfield(L, -3, fname); +#else + lua_pushvalue(L, -1); + lua_rawseti(L, LUA_REGISTRYINDEX, liolib_keys[k]); + lua_setfield(L, -2, fname); +#endif +} + +#if LUA_OPTIMIZE_MEMORY != 2 +static void ICACHE_FLASH_ATTR newfenv (lua_State *L, lua_CFunction cls) { + lua_createtable(L, 0, 1); + lua_pushcfunction(L, cls); + lua_setfield(L, -2, "__close"); +} +#endif + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_io (lua_State *L) { + createmeta(L); +#if LUA_OPTIMIZE_MEMORY != 2 + /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */ + newfenv(L, io_fclose); + lua_replace(L, LUA_ENVIRONINDEX); + /* open library */ + luaL_register(L, LUA_IOLIBNAME, iolib); + newfenv(L, io_noclose); /* close function for default files */ +#else + luaL_register_light(L, LUA_IOLIBNAME, iolib); + lua_pushvalue(L, -1); + lua_setmetatable(L, -2); +#endif +#if 0 + /* create (and set) default files */ + createstdfile(L, c_stdin, IO_INPUT, "stdin"); + createstdfile(L, c_stdout, IO_OUTPUT, "stdout"); + createstdfile(L, c_stderr, IO_STDERR, "stderr"); + +#if LUA_OPTIMIZE_MEMORY != 2 + lua_pop(L, 1); /* pop environment for default files */ + lua_getfield(L, -1, "popen"); + newfenv(L, io_pclose); /* create environment for 'popen' */ + lua_setfenv(L, -2); /* set fenv for 'popen' */ + lua_pop(L, 1); /* pop 'popen' */ +#endif +#endif + return 1; +} diff --git a/app/lua/llex.c b/app/lua/llex.c new file mode 100644 index 00000000..398b3c38 --- /dev/null +++ b/app/lua/llex.c @@ -0,0 +1,458 @@ +/* +** $Id: llex.c,v 2.20.1.2 2009/11/23 14:58:22 roberto Exp $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + + +#include "c_ctype.h" +#include "c_locale.h" +#include "c_string.h" + +#define llex_c +#define LUA_CORE + +#include "lua.h" + +#include "ldo.h" +#include "llex.h" +#include "lobject.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "lzio.h" + + + +#define next(ls) (ls->current = zgetc(ls->z)) + + + + +#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') + + +/* ORDER RESERVED */ +const char *const luaX_tokens [] = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "if", + "in", "local", "nil", "not", "or", "repeat", + "return", "then", "true", "until", "while", + "..", "...", "==", ">=", "<=", "~=", + "", "", "", "", + NULL +}; + + +#define save_and_next(ls) (save(ls, ls->current), next(ls)) + + +static void ICACHE_FLASH_ATTR save (LexState *ls, int c) { + Mbuffer *b = ls->buff; + if (b->n + 1 > b->buffsize) { + size_t newsize; + if (b->buffsize >= MAX_SIZET/2) + luaX_lexerror(ls, "lexical element too long", 0); + newsize = b->buffsize * 2; + luaZ_resizebuffer(ls->L, b, newsize); + } + b->buffer[b->n++] = cast(char, c); +} + + +void ICACHE_FLASH_ATTR luaX_init (lua_State *L) { +} + + +#define MAXSRC 80 + + +const char *ICACHE_FLASH_ATTR luaX_token2str (LexState *ls, int token) { + if (token < FIRST_RESERVED) { + lua_assert(token == cast(unsigned char, token)); + return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) : + luaO_pushfstring(ls->L, "%c", token); + } + else + return luaX_tokens[token-FIRST_RESERVED]; +} + + +static const char *ICACHE_FLASH_ATTR txtToken (LexState *ls, int token) { + switch (token) { + case TK_NAME: + case TK_STRING: + case TK_NUMBER: + save(ls, '\0'); + return luaZ_buffer(ls->buff); + default: + return luaX_token2str(ls, token); + } +} + + +void ICACHE_FLASH_ATTR luaX_lexerror (LexState *ls, const char *msg, int token) { + char buff[MAXSRC]; + luaO_chunkid(buff, getstr(ls->source), MAXSRC); + msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg); + if (token) + luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token)); + luaD_throw(ls->L, LUA_ERRSYNTAX); +} + + +void ICACHE_FLASH_ATTR luaX_syntaxerror (LexState *ls, const char *msg) { + luaX_lexerror(ls, msg, ls->t.token); +} + + +TString *ICACHE_FLASH_ATTR luaX_newstring (LexState *ls, const char *str, size_t l) { + lua_State *L = ls->L; + TString *ts = luaS_newlstr(L, str, l); + TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */ + if (ttisnil(o)) { + setbvalue(o, 1); /* make sure `str' will not be collected */ + luaC_checkGC(L); + } + return ts; +} + + +static void ICACHE_FLASH_ATTR inclinenumber (LexState *ls) { + int old = ls->current; + lua_assert(currIsNewline(ls)); + next(ls); /* skip `\n' or `\r' */ + if (currIsNewline(ls) && ls->current != old) + next(ls); /* skip `\n\r' or `\r\n' */ + if (++ls->linenumber >= MAX_INT) + luaX_syntaxerror(ls, "chunk has too many lines"); +} + + +void ICACHE_FLASH_ATTR luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) { + ls->decpoint = '.'; + ls->L = L; + ls->lookahead.token = TK_EOS; /* no look-ahead token */ + ls->z = z; + ls->fs = NULL; + ls->linenumber = 1; + ls->lastline = 1; + ls->source = source; + luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ + next(ls); /* read first char */ +} + + + +/* +** ======================================================= +** LEXICAL ANALYZER +** ======================================================= +*/ + + + +static int ICACHE_FLASH_ATTR check_next (LexState *ls, const char *set) { + if (!c_strchr(set, ls->current)) + return 0; + save_and_next(ls); + return 1; +} + + +static void ICACHE_FLASH_ATTR buffreplace (LexState *ls, char from, char to) { + size_t n = luaZ_bufflen(ls->buff); + char *p = luaZ_buffer(ls->buff); + while (n--) + if (p[n] == from) p[n] = to; +} + + +static void ICACHE_FLASH_ATTR trydecpoint (LexState *ls, SemInfo *seminfo) { + /* format error: try to update decimal point separator */ + struct lconv *cv = localeconv(); + char old = ls->decpoint; + ls->decpoint = (cv ? cv->decimal_point[0] : '.'); + buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */ + if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) { + /* format error with correct decimal point: no more options */ + buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ + luaX_lexerror(ls, "malformed number", TK_NUMBER); + } +} + + +/* LUA_NUMBER */ +static void ICACHE_FLASH_ATTR read_numeral (LexState *ls, SemInfo *seminfo) { + lua_assert(isdigit(ls->current)); + do { + save_and_next(ls); + } while (isdigit(ls->current) || ls->current == '.'); + if (check_next(ls, "Ee")) /* `E'? */ + check_next(ls, "+-"); /* optional exponent sign */ + while (isalnum(ls->current) || ls->current == '_') + save_and_next(ls); + save(ls, '\0'); + buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ + if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */ + trydecpoint(ls, seminfo); /* try to update decimal point separator */ +} + + +static int ICACHE_FLASH_ATTR skip_sep (LexState *ls) { + int count = 0; + int s = ls->current; + lua_assert(s == '[' || s == ']'); + save_and_next(ls); + while (ls->current == '=') { + save_and_next(ls); + count++; + } + return (ls->current == s) ? count : (-count) - 1; +} + + +static void ICACHE_FLASH_ATTR read_long_string (LexState *ls, SemInfo *seminfo, int sep) { + int cont = 0; + (void)(cont); /* avoid warnings when `cont' is not used */ + save_and_next(ls); /* skip 2nd `[' */ + if (currIsNewline(ls)) /* string starts with a newline? */ + inclinenumber(ls); /* skip it */ + for (;;) { + switch (ls->current) { + case EOZ: + luaX_lexerror(ls, (seminfo) ? "unfinished long string" : + "unfinished long comment", TK_EOS); + break; /* to avoid warnings */ +#if defined(LUA_COMPAT_LSTR) + case '[': { + if (skip_sep(ls) == sep) { + save_and_next(ls); /* skip 2nd `[' */ + cont++; +#if LUA_COMPAT_LSTR == 1 + if (sep == 0) + luaX_lexerror(ls, "nesting of [[...]] is deprecated", '['); +#endif + } + break; + } +#endif + case ']': { + if (skip_sep(ls) == sep) { + save_and_next(ls); /* skip 2nd `]' */ +#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2 + cont--; + if (sep == 0 && cont >= 0) break; +#endif + goto endloop; + } + break; + } + case '\n': + case '\r': { + save(ls, '\n'); + inclinenumber(ls); + if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */ + break; + } + default: { + if (seminfo) save_and_next(ls); + else next(ls); + } + } + } endloop: + if (seminfo) + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep), + luaZ_bufflen(ls->buff) - 2*(2 + sep)); +} + + +static void ICACHE_FLASH_ATTR read_string (LexState *ls, int del, SemInfo *seminfo) { + save_and_next(ls); + while (ls->current != del) { + switch (ls->current) { + case EOZ: + luaX_lexerror(ls, "unfinished string", TK_EOS); + continue; /* to avoid warnings */ + case '\n': + case '\r': + luaX_lexerror(ls, "unfinished string", TK_STRING); + continue; /* to avoid warnings */ + case '\\': { + int c; + next(ls); /* do not save the `\' */ + switch (ls->current) { + case 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case '\n': /* go through */ + case '\r': save(ls, '\n'); inclinenumber(ls); continue; + case EOZ: continue; /* will raise an error next loop */ + default: { + if (!isdigit(ls->current)) + save_and_next(ls); /* handles \\, \", \', and \? */ + else { /* \xxx */ + int i = 0; + c = 0; + do { + c = 10*c + (ls->current-'0'); + next(ls); + } while (++i<3 && isdigit(ls->current)); + if (c > UCHAR_MAX) + luaX_lexerror(ls, "escape sequence too large", TK_STRING); + save(ls, c); + } + continue; + } + } + save(ls, c); + next(ls); + continue; + } + default: + save_and_next(ls); + } + } + save_and_next(ls); /* skip delimiter */ + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1, + luaZ_bufflen(ls->buff) - 2); +} + + +static int ICACHE_FLASH_ATTR llex (LexState *ls, SemInfo *seminfo) { + luaZ_resetbuffer(ls->buff); + for (;;) { + switch (ls->current) { + case '\n': + case '\r': { + inclinenumber(ls); + continue; + } + case '-': { + next(ls); + if (ls->current != '-') return '-'; + /* else is a comment */ + next(ls); + if (ls->current == '[') { + int sep = skip_sep(ls); + luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */ + if (sep >= 0) { + read_long_string(ls, NULL, sep); /* long comment */ + luaZ_resetbuffer(ls->buff); + continue; + } + } + /* else short comment */ + while (!currIsNewline(ls) && ls->current != EOZ) + next(ls); + continue; + } + case '[': { + int sep = skip_sep(ls); + if (sep >= 0) { + read_long_string(ls, seminfo, sep); + return TK_STRING; + } + else if (sep == -1) return '['; + else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING); + } + case '=': { + next(ls); + if (ls->current != '=') return '='; + else { next(ls); return TK_EQ; } + } + case '<': { + next(ls); + if (ls->current != '=') return '<'; + else { next(ls); return TK_LE; } + } + case '>': { + next(ls); + if (ls->current != '=') return '>'; + else { next(ls); return TK_GE; } + } + case '~': { + next(ls); + if (ls->current != '=') return '~'; + else { next(ls); return TK_NE; } + } + case '"': + case '\'': { + read_string(ls, ls->current, seminfo); + return TK_STRING; + } + case '.': { + save_and_next(ls); + if (check_next(ls, ".")) { + if (check_next(ls, ".")) + return TK_DOTS; /* ... */ + else return TK_CONCAT; /* .. */ + } + else if (!isdigit(ls->current)) return '.'; + else { + read_numeral(ls, seminfo); + return TK_NUMBER; + } + } + case EOZ: { + return TK_EOS; + } + default: { + if (isspace(ls->current)) { + lua_assert(!currIsNewline(ls)); + next(ls); + continue; + } + else if (isdigit(ls->current)) { + read_numeral(ls, seminfo); + return TK_NUMBER; + } + else if (isalpha(ls->current) || ls->current == '_') { + /* identifier or reserved word */ + TString *ts; + int i; + do { + save_and_next(ls); + } while (isalnum(ls->current) || ls->current == '_'); + /* look for reserved word */ + save(ls, '\0'); + for (i = 0; i < NUM_RESERVED; i++) + if (!c_strcmp(luaX_tokens[i], luaZ_buffer(ls->buff))) + return i + FIRST_RESERVED; + ts = luaX_newstring(ls, luaZ_buffer(ls->buff), + luaZ_bufflen(ls->buff) - 1); + seminfo->ts = ts; + return TK_NAME; + } + else { + int c = ls->current; + next(ls); + return c; /* single-char tokens (+ - / ...) */ + } + } + } + } +} + + +void ICACHE_FLASH_ATTR luaX_next (LexState *ls) { + ls->lastline = ls->linenumber; + if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ + ls->t = ls->lookahead; /* use this one */ + ls->lookahead.token = TK_EOS; /* and discharge it */ + } + else + ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ +} + + +void ICACHE_FLASH_ATTR luaX_lookahead (LexState *ls) { + lua_assert(ls->lookahead.token == TK_EOS); + ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); +} + diff --git a/app/lua/llex.h b/app/lua/llex.h new file mode 100644 index 00000000..a9201cee --- /dev/null +++ b/app/lua/llex.h @@ -0,0 +1,81 @@ +/* +** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + +#ifndef llex_h +#define llex_h + +#include "lobject.h" +#include "lzio.h" + + +#define FIRST_RESERVED 257 + +/* maximum length of a reserved word */ +#define TOKEN_LEN (sizeof("function")/sizeof(char)) + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER RESERVED" +*/ +enum RESERVED { + /* terminal symbols denoted by reserved words */ + TK_AND = FIRST_RESERVED, TK_BREAK, + TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, + TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, + TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, + /* other terminal symbols */ + TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER, + TK_NAME, TK_STRING, TK_EOS +}; + +/* number of reserved words */ +#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1)) + + +/* array with token `names' */ +LUAI_DATA const char *const luaX_tokens []; + + +typedef union { + lua_Number r; + TString *ts; +} SemInfo; /* semantics information */ + + +typedef struct Token { + int token; + SemInfo seminfo; +} Token; + + +typedef struct LexState { + int current; /* current character (charint) */ + int linenumber; /* input line counter */ + int lastline; /* line of last token `consumed' */ + Token t; /* current token */ + Token lookahead; /* look ahead token */ + struct FuncState *fs; /* `FuncState' is private to the parser */ + struct lua_State *L; + ZIO *z; /* input stream */ + Mbuffer *buff; /* buffer for tokens */ + TString *source; /* current source name */ + char decpoint; /* locale decimal point */ +} LexState; + + +LUAI_FUNC void luaX_init (lua_State *L); +LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, + TString *source); +LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); +LUAI_FUNC void luaX_next (LexState *ls); +LUAI_FUNC void luaX_lookahead (LexState *ls); +LUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token); +LUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s); +LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); + + +#endif diff --git a/app/lua/llimits.h b/app/lua/llimits.h new file mode 100644 index 00000000..246f13ab --- /dev/null +++ b/app/lua/llimits.h @@ -0,0 +1,128 @@ +/* +** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $ +** Limits, basic types, and some other `installation-dependent' definitions +** See Copyright Notice in lua.h +*/ + +#ifndef llimits_h +#define llimits_h + + +//#include "c_limits.h" +//#include "c_stddef.h" + + +#include "lua.h" + + +typedef LUAI_UINT32 lu_int32; + +typedef LUAI_UMEM lu_mem; + +typedef LUAI_MEM l_mem; + + + +/* chars used as small naturals (so that `char' is reserved for characters) */ +typedef unsigned char lu_byte; + + +#define MAX_SIZET ((size_t)(~(size_t)0)-2) + +#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2) + + +#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ + +/* +** conversion of pointer to integer +** this is for hashing only; there is no problem if the integer +** cannot hold the whole pointer value +*/ +#define IntPoint(p) ((unsigned int)(lu_mem)(p)) + + + +/* type to ensure maximum alignment */ +typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; + + +/* result of a `usual argument conversion' over lua_Number */ +typedef LUAI_UACNUMBER l_uacNumber; + + +/* internal assertions for in-house debugging */ +#ifdef lua_assert + +#define check_exp(c,e) (lua_assert(c), (e)) +#define api_check(l,e) lua_assert(e) + +#else + +#define lua_assert(c) ((void)0) +#define check_exp(c,e) (e) +#define api_check luai_apicheck + +#endif + + +#ifndef UNUSED +#define UNUSED(x) ((void)(x)) /* to avoid warnings */ +#endif + + +#ifndef cast +#define cast(t, exp) ((t)(exp)) +#endif + +#define cast_byte(i) cast(lu_byte, (i)) +#define cast_num(i) cast(lua_Number, (i)) +#define cast_int(i) cast(int, (i)) + + + +/* +** type for virtual-machine instructions +** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) +*/ +typedef lu_int32 Instruction; + + + +/* maximum stack for a Lua function */ +#define MAXSTACK 250 + + + +/* minimum size for the string table (must be power of 2) */ +#ifndef MINSTRTABSIZE +#define MINSTRTABSIZE 32 +#endif + + +/* minimum size for string buffer */ +#ifndef LUA_MINBUFFER +#define LUA_MINBUFFER 32 +#endif + + +#ifndef lua_lock +#define lua_lock(L) ((void) 0) +#define lua_unlock(L) ((void) 0) +#endif + +#ifndef luai_threadyield +#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} +#endif + + +/* +** macro to control inclusion of some hard tests on stack reallocation +*/ +#ifndef HARDSTACKTESTS +#define condhardstacktests(x) ((void)0) +#else +#define condhardstacktests(x) x +#endif + +#endif diff --git a/app/lua/lmathlib.c b/app/lua/lmathlib.c new file mode 100644 index 00000000..bdcad18f --- /dev/null +++ b/app/lua/lmathlib.c @@ -0,0 +1,394 @@ +/* +** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $ +** Standard mathematical library +** See Copyright Notice in lua.h +*/ + + +#include "c_stdlib.h" +#include "c_math.h" + +#define lmathlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "lrotable.h" + +#undef PI +#define PI (3.14159265358979323846) +#define RADIANS_PER_DEGREE (PI/180.0) + + + +static int ICACHE_FLASH_ATTR math_abs (lua_State *L) { +#ifdef LUA_NUMBER_INTEGRAL + lua_Number x = luaL_checknumber(L, 1); + if (x < 0) x = -x; //fails for -2^31 + lua_pushnumber(L, x); +#else + lua_pushnumber(L, fabs(luaL_checknumber(L, 1))); +#endif + return 1; +} + +#ifndef LUA_NUMBER_INTEGRAL + +static int ICACHE_FLASH_ATTR math_sin (lua_State *L) { + lua_pushnumber(L, sin(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_sinh (lua_State *L) { + lua_pushnumber(L, sinh(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_cos (lua_State *L) { + lua_pushnumber(L, cos(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_cosh (lua_State *L) { + lua_pushnumber(L, cosh(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_tan (lua_State *L) { + lua_pushnumber(L, tan(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_tanh (lua_State *L) { + lua_pushnumber(L, tanh(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_asin (lua_State *L) { + lua_pushnumber(L, asin(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_acos (lua_State *L) { + lua_pushnumber(L, acos(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_atan (lua_State *L) { + lua_pushnumber(L, atan(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_atan2 (lua_State *L) { + lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_ceil (lua_State *L) { + lua_pushnumber(L, ceil(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_floor (lua_State *L) { + lua_pushnumber(L, floor(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_fmod (lua_State *L) { + lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_modf (lua_State *L) { + double ip; + double fp = modf(luaL_checknumber(L, 1), &ip); + lua_pushnumber(L, ip); + lua_pushnumber(L, fp); + return 2; +} + +#else // #ifndef LUA_NUMBER_INTEGRAL + +// In integer math, floor() and ceil() give the same value; +// having them in the integer library allows you to write code that +// works in both integer and floating point versions of Lua. +// This identity function is used for them. + +static int ICACHE_FLASH_ATTR math_identity (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)); + return 1; +} + +#endif // #ifndef LUA_NUMBER_INTEGRAL + +#ifdef LUA_NUMBER_INTEGRAL +// Integer square root for integer version +static lua_Number ICACHE_FLASH_ATTR isqrt(lua_Number x) +{ + lua_Number op, res, one; + + op = x; res = 0; + + /* "one" starts at the highest power of four <= than the argument. */ + one = 1 << 30; /* second-to-top bit set */ + while (one > op) one >>= 2; + + while (one != 0) { + if (op >= res + one) { + op = op - (res + one); + res = res + 2 * one; + } + res >>= 1; + one >>= 2; + } + return(res); +} +#endif + +static int ICACHE_FLASH_ATTR math_sqrt (lua_State *L) { +#ifdef LUA_NUMBER_INTEGRAL + lua_Number x = luaL_checknumber(L, 1); + luaL_argcheck(L, 0<=x, 1, "negative"); + lua_pushnumber(L, isqrt(x)); +#else + lua_pushnumber(L, sqrt(luaL_checknumber(L, 1))); +#endif + return 1; +} + +#ifdef LUA_NUMBER_INTEGRAL +# define pow(a,b) luai_ipow(a,b) +#endif + +static int ICACHE_FLASH_ATTR math_pow (lua_State *L) { + lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +#ifdef LUA_NUMBER_INTEGRAL +# undef pow +#endif + + +#ifndef LUA_NUMBER_INTEGRAL + +static int ICACHE_FLASH_ATTR math_log (lua_State *L) { + lua_pushnumber(L, log(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_log10 (lua_State *L) { + lua_pushnumber(L, log10(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_exp (lua_State *L) { + lua_pushnumber(L, exp(luaL_checknumber(L, 1))); + return 1; +} + +static int ICACHE_FLASH_ATTR math_deg (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); + return 1; +} + +static int ICACHE_FLASH_ATTR math_rad (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); + return 1; +} + +static int ICACHE_FLASH_ATTR math_frexp (lua_State *L) { + int e; + lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e)); + lua_pushinteger(L, e); + return 2; +} + +static int ICACHE_FLASH_ATTR math_ldexp (lua_State *L) { + lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2))); + return 1; +} + +#endif // #ifdef LUA_NUMBER_INTEGRAL + +static int ICACHE_FLASH_ATTR math_min (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmin = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d < dmin) + dmin = d; + } + lua_pushnumber(L, dmin); + return 1; +} + + +static int ICACHE_FLASH_ATTR math_max (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmax = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d > dmax) + dmax = d; + } + lua_pushnumber(L, dmax); + return 1; +} + + +#ifdef LUA_NUMBER_INTEGRAL + +static int ICACHE_FLASH_ATTR math_random (lua_State *L) { + lua_Number r = (lua_Number)(rand()%RAND_MAX); + + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, 0); /* Number between 0 and 1 - always 0 with ints */ + break; + } + case 1: { /* only upper limit */ + int u = luaL_checkint(L, 1); + luaL_argcheck(L, 1<=u, 1, "interval is empty"); + lua_pushnumber(L, (r % u)+1); /* int between 1 and `u' */ + break; + } + case 2: { /* lower and upper limits */ + int l = luaL_checkint(L, 1); + int u = luaL_checkint(L, 2); + luaL_argcheck(L, l<=u, 2, "interval is empty"); + lua_pushnumber(L, (r%(u-l+1))+l); /* int between `l' and `u' */ + break; + } + default: return luaL_error(L, "wrong number of arguments"); + } + return 1; +} + +#else + +static int ICACHE_FLASH_ATTR math_random (lua_State *L) { + /* the `%' avoids the (rare) case of r==1, and is needed also because on + some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ + lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, r); /* Number between 0 and 1 */ + break; + } + case 1: { /* only upper limit */ + int u = luaL_checkint(L, 1); + luaL_argcheck(L, 1<=u, 1, "interval is empty"); + lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */ + break; + } + case 2: { /* lower and upper limits */ + int l = luaL_checkint(L, 1); + int u = luaL_checkint(L, 2); + luaL_argcheck(L, l<=u, 2, "interval is empty"); + lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */ + break; + } + default: return luaL_error(L, "wrong number of arguments"); + } + return 1; +} + +#endif + + +static int ICACHE_FLASH_ATTR math_randomseed (lua_State *L) { + srand(luaL_checkint(L, 1)); + return 0; +} + +#define MIN_OPT_LEVEL 1 +#include "lrodefs.h" +const LUA_REG_TYPE math_map[] = { +#ifdef LUA_NUMBER_INTEGRAL + {LSTRKEY("abs"), LFUNCVAL(math_abs)}, + {LSTRKEY("ceil"), LFUNCVAL(math_identity)}, + {LSTRKEY("floor"), LFUNCVAL(math_identity)}, + {LSTRKEY("max"), LFUNCVAL(math_max)}, + {LSTRKEY("min"), LFUNCVAL(math_min)}, + {LSTRKEY("pow"), LFUNCVAL(math_pow)}, + {LSTRKEY("random"), LFUNCVAL(math_random)}, + {LSTRKEY("randomseed"), LFUNCVAL(math_randomseed)}, + {LSTRKEY("sqrt"), LFUNCVAL(math_sqrt)}, +#if LUA_OPTIMIZE_MEMORY > 0 + {LSTRKEY("huge"), LNUMVAL(LONG_MAX)}, +#endif +#else + {LSTRKEY("abs"), LFUNCVAL(math_abs)}, + {LSTRKEY("acos"), LFUNCVAL(math_acos)}, + {LSTRKEY("asin"), LFUNCVAL(math_asin)}, + {LSTRKEY("atan2"), LFUNCVAL(math_atan2)}, + {LSTRKEY("atan"), LFUNCVAL(math_atan)}, + {LSTRKEY("ceil"), LFUNCVAL(math_ceil)}, + {LSTRKEY("cosh"), LFUNCVAL(math_cosh)}, + {LSTRKEY("cos"), LFUNCVAL(math_cos)}, + {LSTRKEY("deg"), LFUNCVAL(math_deg)}, + {LSTRKEY("exp"), LFUNCVAL(math_exp)}, + {LSTRKEY("floor"), LFUNCVAL(math_floor)}, + {LSTRKEY("fmod"), LFUNCVAL(math_fmod)}, +#if LUA_OPTIMIZE_MEMORY > 0 && defined(LUA_COMPAT_MOD) + {LSTRKEY("mod"), LFUNCVAL(math_fmod)}, +#endif + {LSTRKEY("frexp"), LFUNCVAL(math_frexp)}, + {LSTRKEY("ldexp"), LFUNCVAL(math_ldexp)}, + {LSTRKEY("log10"), LFUNCVAL(math_log10)}, + {LSTRKEY("log"), LFUNCVAL(math_log)}, + {LSTRKEY("max"), LFUNCVAL(math_max)}, + {LSTRKEY("min"), LFUNCVAL(math_min)}, + {LSTRKEY("modf"), LFUNCVAL(math_modf)}, + {LSTRKEY("pow"), LFUNCVAL(math_pow)}, + {LSTRKEY("rad"), LFUNCVAL(math_rad)}, + {LSTRKEY("random"), LFUNCVAL(math_random)}, + {LSTRKEY("randomseed"), LFUNCVAL(math_randomseed)}, + {LSTRKEY("sinh"), LFUNCVAL(math_sinh)}, + {LSTRKEY("sin"), LFUNCVAL(math_sin)}, + {LSTRKEY("sqrt"), LFUNCVAL(math_sqrt)}, + {LSTRKEY("tanh"), LFUNCVAL(math_tanh)}, + {LSTRKEY("tan"), LFUNCVAL(math_tan)}, +#if LUA_OPTIMIZE_MEMORY > 0 + {LSTRKEY("pi"), LNUMVAL(PI)}, + {LSTRKEY("huge"), LNUMVAL(HUGE_VAL)}, +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +#endif // #ifdef LUA_NUMBER_INTEGRAL + {LNILKEY, LNILVAL} +}; + + +/* +** Open math library +*/ + +#if defined LUA_NUMBER_INTEGRAL +# include "c_limits.h" /* for LONG_MAX */ +#endif + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_math (lua_State *L) { +#if LUA_OPTIMIZE_MEMORY > 0 + return 0; +#else + luaL_register(L, LUA_MATHLIBNAME, math_map); +# if defined LUA_NUMBER_INTEGRAL + lua_pushnumber(L, LONG_MAX); + lua_setfield(L, -2, "huge"); +# else + lua_pushnumber(L, PI); + lua_setfield(L, -2, "pi"); + lua_pushnumber(L, HUGE_VAL); + lua_setfield(L, -2, "huge"); +# if defined(LUA_COMPAT_MOD) + lua_getfield(L, -1, "fmod"); + lua_setfield(L, -2, "mod"); +# endif +# endif + return 1; +#endif +} diff --git a/app/lua/lmem.c b/app/lua/lmem.c new file mode 100644 index 00000000..eb07011e --- /dev/null +++ b/app/lua/lmem.c @@ -0,0 +1,86 @@ +/* +** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + + +#include "c_stddef.h" + +#define lmem_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +/* +** About the realloc function: +** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); +** (`osize' is the old size, `nsize' is the new size) +** +** Lua ensures that (ptr == NULL) iff (osize == 0). +** +** * frealloc(ud, NULL, 0, x) creates a new block of size `x' +** +** * frealloc(ud, p, x, 0) frees the block `p' +** (in this specific case, frealloc must return NULL). +** particularly, frealloc(ud, NULL, 0, 0) does nothing +** (which is equivalent to free(NULL) in ANSI C) +** +** frealloc returns NULL if it cannot create or reallocate the area +** (any reallocation to an equal or smaller size cannot fail!) +*/ + + + +#define MINSIZEARRAY 4 + + +void *ICACHE_FLASH_ATTR luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems, + int limit, const char *errormsg) { + void *newblock; + int newsize; + if (*size >= limit/2) { /* cannot double it? */ + if (*size >= limit) /* cannot grow even a little? */ + luaG_runerror(L, errormsg); + newsize = limit; /* still have at least one free place */ + } + else { + newsize = (*size)*2; + if (newsize < MINSIZEARRAY) + newsize = MINSIZEARRAY; /* minimum size */ + } + newblock = luaM_reallocv(L, block, *size, newsize, size_elems); + *size = newsize; /* update only when everything else is OK */ + return newblock; +} + + +void *ICACHE_FLASH_ATTR luaM_toobig (lua_State *L) { + luaG_runerror(L, "memory allocation error: block too big"); + return NULL; /* to avoid warnings */ +} + + + +/* +** generic allocation routine. +*/ +void *ICACHE_FLASH_ATTR luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { + global_State *g = G(L); + lua_assert((osize == 0) == (block == NULL)); + block = (*g->frealloc)(g->ud, block, osize, nsize); + if (block == NULL && nsize > 0) + luaD_throw(L, LUA_ERRMEM); + lua_assert((nsize == 0) == (block == NULL)); + g->totalbytes = (g->totalbytes - osize) + nsize; + return block; +} + diff --git a/app/lua/lmem.h b/app/lua/lmem.h new file mode 100644 index 00000000..f6938452 --- /dev/null +++ b/app/lua/lmem.h @@ -0,0 +1,49 @@ +/* +** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + +#ifndef lmem_h +#define lmem_h + + +//#include "c_stddef.h" + +#include "llimits.h" +#include "lua.h" + +#define MEMERRMSG "not enough memory" + + +#define luaM_reallocv(L,b,on,n,e) \ + ((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ? /* +1 to avoid warnings */ \ + luaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \ + luaM_toobig(L)) + +#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0) +#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0) +#define luaM_freearray(L, b, n, t) luaM_reallocv(L, (b), n, 0, sizeof(t)) + +#define luaM_malloc(L,t) luaM_realloc_(L, NULL, 0, (t)) +#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) +#define luaM_newvector(L,n,t) \ + cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t))) + +#define luaM_growvector(L,v,nelems,size,t,limit,e) \ + if ((nelems)+1 > (size)) \ + ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e))) + +#define luaM_reallocvector(L, v,oldn,n,t) \ + ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t)))) + + +LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, + size_t size); +LUAI_FUNC void *luaM_toobig (lua_State *L); +LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size, + size_t size_elem, int limit, + const char *errormsg); + +#endif + diff --git a/app/lua/loadlib.c b/app/lua/loadlib.c new file mode 100644 index 00000000..0b3dc7e0 --- /dev/null +++ b/app/lua/loadlib.c @@ -0,0 +1,695 @@ +/* +** $Id: loadlib.c,v 1.52.1.4 2009/09/09 13:17:16 roberto Exp $ +** Dynamic library loader for Lua +** See Copyright Notice in lua.h +** +** This module contains an implementation of loadlib for Unix systems +** that have dlfcn, an implementation for Darwin (Mac OS X), an +** implementation for Windows, and a stub for other systems. +*/ + + +#include "c_stdlib.h" +#include "c_string.h" +#include "c_fcntl.h" +#include "flash_fs.h" + +#define loadlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "lrotable.h" + +/* prefix for open functions in C libraries */ +#define LUA_POF "luaopen_" + +/* separator for open functions in C libraries */ +#define LUA_OFSEP "_" + + +#define LIBPREFIX "LOADLIB: " + +#define POF LUA_POF +#define LIB_FAIL "open" + + +/* error codes for ll_loadfunc */ +#define ERRLIB 1 +#define ERRFUNC 2 + +#define setprogdir(L) ((void)0) + + +static void ll_unloadlib (void *lib); +static void *ll_load (lua_State *L, const char *path); +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym); + + + +#if defined(LUA_DL_DLOPEN) +/* +** {======================================================================== +** This is an implementation of loadlib based on the dlfcn interface. +** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +** as an emulation layer on top of native functions. +** ========================================================================= +*/ + +#include + +static void ll_unloadlib (void *lib) { + dlclose(lib); +} + + +static void *ll_load (lua_State *L, const char *path) { + void *lib = dlopen(path, RTLD_NOW); + if (lib == NULL) lua_pushstring(L, dlerror()); + return lib; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)dlsym(lib, sym); + if (f == NULL) lua_pushstring(L, dlerror()); + return f; +} + +/* }====================================================== */ + + + +#elif defined(LUA_DL_DLL) +/* +** {====================================================================== +** This is an implementation of loadlib for Windows using native functions. +** ======================================================================= +*/ + +#include + + +#undef setprogdir + +static void setprogdir (lua_State *L) { + char buff[MAX_PATH + 1]; + char *lb; + DWORD nsize = sizeof(buff)/sizeof(char); + DWORD n = GetModuleFileNameA(NULL, buff, nsize); + if (n == 0 || n == nsize || (lb = c_strrchr(buff, '\\')) == NULL) + luaL_error(L, "unable to get ModuleFileName"); + else { + *lb = '\0'; + luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff); + lua_remove(L, -2); /* remove original string */ + } +} + + +static void pusherror (lua_State *L) { + int error = GetLastError(); + char buffer[128]; + if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, 0, buffer, sizeof(buffer), NULL)) + lua_pushstring(L, buffer); + else + lua_pushfstring(L, "system error %d\n", error); +} + +static void ll_unloadlib (void *lib) { + FreeLibrary((HINSTANCE)lib); +} + + +static void *ll_load (lua_State *L, const char *path) { + HINSTANCE lib = LoadLibraryA(path); + if (lib == NULL) pusherror(L); + return lib; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym); + if (f == NULL) pusherror(L); + return f; +} + +/* }====================================================== */ + + + +#elif defined(LUA_DL_DYLD) +/* +** {====================================================================== +** Native Mac OS X / Darwin Implementation +** ======================================================================= +*/ + +#include + + +/* Mac appends a `_' before C function names */ +#undef POF +#define POF "_" LUA_POF + + +static void pusherror (lua_State *L) { + const char *err_str; + const char *err_file; + NSLinkEditErrors err; + int err_num; + NSLinkEditError(&err, &err_num, &err_file, &err_str); + lua_pushstring(L, err_str); +} + + +static const char *errorfromcode (NSObjectFileImageReturnCode ret) { + switch (ret) { + case NSObjectFileImageInappropriateFile: + return "file is not a bundle"; + case NSObjectFileImageArch: + return "library is for wrong CPU type"; + case NSObjectFileImageFormat: + return "bad format"; + case NSObjectFileImageAccess: + return "cannot access file"; + case NSObjectFileImageFailure: + default: + return "unable to load library"; + } +} + + +static void ll_unloadlib (void *lib) { + NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES); +} + + +static void *ll_load (lua_State *L, const char *path) { + NSObjectFileImage img; + NSObjectFileImageReturnCode ret; + /* this would be a rare case, but prevents crashing if it happens */ + if(!_dyld_present()) { + lua_pushliteral(L, "dyld not present"); + return NULL; + } + ret = NSCreateObjectFileImageFromFile(path, &img); + if (ret == NSObjectFileImageSuccess) { + NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE | + NSLINKMODULE_OPTION_RETURN_ON_ERROR); + NSDestroyObjectFileImage(img); + if (mod == NULL) pusherror(L); + return mod; + } + lua_pushstring(L, errorfromcode(ret)); + return NULL; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym); + if (nss == NULL) { + lua_pushfstring(L, "symbol " LUA_QS " not found", sym); + return NULL; + } + return (lua_CFunction)NSAddressOfSymbol(nss); +} + +/* }====================================================== */ + + + +#else +/* +** {====================================================== +** Fallback for other systems +** ======================================================= +*/ + +#undef LIB_FAIL +#define LIB_FAIL "absent" + + +#define DLMSG "dynamic libraries not enabled; check your Lua installation" + + +static void ICACHE_FLASH_ATTR ll_unloadlib (void *lib) { + (void)lib; /* to avoid warnings */ +} + + +static void * ICACHE_FLASH_ATTR ll_load (lua_State *L, const char *path) { + (void)path; /* to avoid warnings */ + lua_pushliteral(L, DLMSG); + return NULL; +} + + +static lua_CFunction ICACHE_FLASH_ATTR ll_sym (lua_State *L, void *lib, const char *sym) { + (void)lib; (void)sym; /* to avoid warnings */ + lua_pushliteral(L, DLMSG); + return NULL; +} + +/* }====================================================== */ +#endif + + + +static void ** ICACHE_FLASH_ATTR ll_register (lua_State *L, const char *path) { + void **plib; + lua_pushfstring(L, "%s%s", LIBPREFIX, path); + lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */ + if (!lua_isnil(L, -1)) /* is there an entry? */ + plib = (void **)lua_touserdata(L, -1); + else { /* no entry yet; create one */ + lua_pop(L, 1); + plib = (void **)lua_newuserdata(L, sizeof(const void *)); + *plib = NULL; + luaL_getmetatable(L, "_LOADLIB"); + lua_setmetatable(L, -2); + lua_pushfstring(L, "%s%s", LIBPREFIX, path); + lua_pushvalue(L, -2); + lua_settable(L, LUA_REGISTRYINDEX); + } + return plib; +} + + +/* +** __gc tag method: calls library's `ll_unloadlib' function with the lib +** handle +*/ +static int ICACHE_FLASH_ATTR gctm (lua_State *L) { + void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB"); + if (*lib) ll_unloadlib(*lib); + *lib = NULL; /* mark library as closed */ + return 0; +} + + +static int ICACHE_FLASH_ATTR ll_loadfunc (lua_State *L, const char *path, const char *sym) { + void **reg = ll_register(L, path); + if (*reg == NULL) *reg = ll_load(L, path); + if (*reg == NULL) + return ERRLIB; /* unable to load library */ + else { + lua_CFunction f = ll_sym(L, *reg, sym); + if (f == NULL) + return ERRFUNC; /* unable to find function */ + lua_pushcfunction(L, f); + return 0; /* return function */ + } +} + + +static int ICACHE_FLASH_ATTR ll_loadlib (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + const char *init = luaL_checkstring(L, 2); + int stat = ll_loadfunc(L, path, init); + if (stat == 0) /* no errors? */ + return 1; /* return the loaded function */ + else { /* error; error message is on stack top */ + lua_pushnil(L); + lua_insert(L, -2); + lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init"); + return 3; /* return nil, error message, and where */ + } +} + + + +/* +** {====================================================== +** 'require' function +** ======================================================= +*/ +#if 0 +static int readable (const char *filename) { + FILE *f = c_fopen(filename, "r"); /* try to open file */ + if (f == NULL) return 0; /* open failed */ + c_fclose(f); + return 1; +} +#else +static int ICACHE_FLASH_ATTR readable (const char *filename) { + int f = fs_open(filename, FS_RDONLY); /* try to open file */ + if (f < FS_OPEN_OK) return 0; /* open failed */ + fs_close(f); + return 1; +} +#endif + +static const char * ICACHE_FLASH_ATTR pushnexttemplate (lua_State *L, const char *path) { + const char *l; + while (*path == *LUA_PATHSEP) path++; /* skip separators */ + if (*path == '\0') return NULL; /* no more templates */ + l = c_strchr(path, *LUA_PATHSEP); /* find next separator */ + if (l == NULL) l = path + c_strlen(path); + lua_pushlstring(L, path, l - path); /* template */ + return l; +} + + +static const char * ICACHE_FLASH_ATTR findfile (lua_State *L, const char *name, + const char *pname) { + const char *path; + name = luaL_gsub(L, name, ".", LUA_DIRSEP); + lua_getfield(L, LUA_ENVIRONINDEX, pname); + path = lua_tostring(L, -1); + if (path == NULL) + luaL_error(L, LUA_QL("package.%s") " must be a string", pname); + lua_pushliteral(L, ""); /* error accumulator */ + while ((path = pushnexttemplate(L, path)) != NULL) { + const char *filename; + filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name); + lua_remove(L, -2); /* remove path template */ + if (readable(filename)) /* does file exist and is readable? */ + return filename; /* return that file name */ + lua_pushfstring(L, "\n\tno file " LUA_QS, filename); + lua_remove(L, -2); /* remove file name */ + lua_concat(L, 2); /* add entry to possible error message */ + } + return NULL; /* not found */ +} + + +static void ICACHE_FLASH_ATTR loaderror (lua_State *L, const char *filename) { + luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s", + lua_tostring(L, 1), filename, lua_tostring(L, -1)); +} + + +static int ICACHE_FLASH_ATTR loader_Lua (lua_State *L) { + const char *filename; + const char *name = luaL_checkstring(L, 1); + filename = findfile(L, name, "path"); + if (filename == NULL) return 1; /* library not found in this path */ +#if 0 + if (luaL_loadfile(L, filename) != 0) +#else + if (luaL_loadfsfile(L, filename) != 0) +#endif + loaderror(L, filename); + return 1; /* library loaded successfully */ +} + + +static const char *mkfuncname (lua_State *L, const char *modname) { + const char *funcname; + const char *mark = c_strchr(modname, *LUA_IGMARK); + if (mark) modname = mark + 1; + funcname = luaL_gsub(L, modname, ".", LUA_OFSEP); + funcname = lua_pushfstring(L, POF"%s", funcname); + lua_remove(L, -2); /* remove 'gsub' result */ + return funcname; +} + + +static int ICACHE_FLASH_ATTR loader_C (lua_State *L) { + const char *funcname; + const char *name = luaL_checkstring(L, 1); + const char *filename = findfile(L, name, "cpath"); + if (filename == NULL) return 1; /* library not found in this path */ + funcname = mkfuncname(L, name); + if (ll_loadfunc(L, filename, funcname) != 0) + loaderror(L, filename); + return 1; /* library loaded successfully */ +} + + +static int ICACHE_FLASH_ATTR loader_Croot (lua_State *L) { + const char *funcname; + const char *filename; + const char *name = luaL_checkstring(L, 1); + const char *p = c_strchr(name, '.'); + int stat; + if (p == NULL) return 0; /* is root */ + lua_pushlstring(L, name, p - name); + filename = findfile(L, lua_tostring(L, -1), "cpath"); + if (filename == NULL) return 1; /* root not found */ + funcname = mkfuncname(L, name); + if ((stat = ll_loadfunc(L, filename, funcname)) != 0) { + if (stat != ERRFUNC) loaderror(L, filename); /* real error */ + lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS, + name, filename); + return 1; /* function not found */ + } + return 1; +} + + +static int ICACHE_FLASH_ATTR loader_preload (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + lua_getfield(L, LUA_ENVIRONINDEX, "preload"); + if (!lua_istable(L, -1)) + luaL_error(L, LUA_QL("package.preload") " must be a table"); + lua_getfield(L, -1, name); + if (lua_isnil(L, -1)) /* not found? */ + lua_pushfstring(L, "\n\tno field package.preload['%s']", name); + return 1; +} + + +static const int sentinel_ = 0; +#define sentinel ((void *)&sentinel_) + + +static int ICACHE_FLASH_ATTR ll_require (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + int i; + lua_settop(L, 1); /* _LOADED table will be at index 2 */ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, 2, name); + if (lua_toboolean(L, -1)) { /* is it there? */ + if (lua_touserdata(L, -1) == sentinel) /* check loops */ + luaL_error(L, "loop or previous error loading module " LUA_QS, name); + return 1; /* package is already loaded */ + } + /* Is this a readonly table? */ + void *res = luaR_findglobal(name, c_strlen(name)); + if (res) { + lua_pushrotable(L, res); + return 1; + } + /* else must load it; iterate over available loaders */ + lua_getfield(L, LUA_ENVIRONINDEX, "loaders"); + if (!lua_istable(L, -1)) + luaL_error(L, LUA_QL("package.loaders") " must be a table"); + lua_pushliteral(L, ""); /* error message accumulator */ + for (i=1; ; i++) { + lua_rawgeti(L, -2, i); /* get a loader */ + if (lua_isnil(L, -1)) + luaL_error(L, "module " LUA_QS " not found:%s", + name, lua_tostring(L, -2)); + lua_pushstring(L, name); + lua_call(L, 1, 1); /* call it */ + if (lua_isfunction(L, -1)) /* did it find module? */ + break; /* module loaded successfully */ + else if (lua_isstring(L, -1)) /* loader returned error message? */ + lua_concat(L, 2); /* accumulate it */ + else + lua_pop(L, 1); + } + lua_pushlightuserdata(L, sentinel); + lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */ + lua_pushstring(L, name); /* pass name as argument to module */ + lua_call(L, 1, 1); /* run loaded module */ + if (!lua_isnil(L, -1)) /* non-nil return? */ + lua_setfield(L, 2, name); /* _LOADED[name] = returned value */ + lua_getfield(L, 2, name); + if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */ + lua_pushboolean(L, 1); /* use true as result */ + lua_pushvalue(L, -1); /* extra copy to be returned */ + lua_setfield(L, 2, name); /* _LOADED[name] = true */ + } + return 1; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** 'module' function +** ======================================================= +*/ + + +static void ICACHE_FLASH_ATTR setfenv (lua_State *L) { + lua_Debug ar; + if (lua_getstack(L, 1, &ar) == 0 || + lua_getinfo(L, "f", &ar) == 0 || /* get calling function */ + lua_iscfunction(L, -1)) + luaL_error(L, LUA_QL("module") " not called from a Lua function"); + lua_pushvalue(L, -2); + lua_setfenv(L, -2); + lua_pop(L, 1); +} + + +static void ICACHE_FLASH_ATTR dooptions (lua_State *L, int n) { + int i; + for (i = 2; i <= n; i++) { + lua_pushvalue(L, i); /* get option (a function) */ + lua_pushvalue(L, -2); /* module */ + lua_call(L, 1, 0); + } +} + + +static void ICACHE_FLASH_ATTR modinit (lua_State *L, const char *modname) { + const char *dot; + lua_pushvalue(L, -1); + lua_setfield(L, -2, "_M"); /* module._M = module */ + lua_pushstring(L, modname); + lua_setfield(L, -2, "_NAME"); + dot = c_strrchr(modname, '.'); /* look for last dot in module name */ + if (dot == NULL) dot = modname; + else dot++; + /* set _PACKAGE as package name (full module name minus last part) */ + lua_pushlstring(L, modname, dot - modname); + lua_setfield(L, -2, "_PACKAGE"); +} + + +static int ICACHE_FLASH_ATTR ll_module (lua_State *L) { + const char *modname = luaL_checkstring(L, 1); + if (luaR_findglobal(modname, c_strlen(modname))) + return 0; + int loaded = lua_gettop(L) + 1; /* index of _LOADED table */ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, loaded, modname); /* get _LOADED[modname] */ + if (!lua_istable(L, -1)) { /* not found? */ + lua_pop(L, 1); /* remove previous result */ + /* try global variable (and create one if it does not exist) */ + if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL) + return luaL_error(L, "name conflict for module " LUA_QS, modname); + lua_pushvalue(L, -1); + lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */ + } + /* check whether table already has a _NAME field */ + lua_getfield(L, -1, "_NAME"); + if (!lua_isnil(L, -1)) /* is table an initialized module? */ + lua_pop(L, 1); + else { /* no; initialize it */ + lua_pop(L, 1); + modinit(L, modname); + } + lua_pushvalue(L, -1); + setfenv(L); + dooptions(L, loaded - 1); + return 0; +} + + +static int ll_seeall (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + if (!lua_getmetatable(L, 1)) { + lua_createtable(L, 0, 1); /* create new metatable */ + lua_pushvalue(L, -1); + lua_setmetatable(L, 1); + } + lua_pushvalue(L, LUA_GLOBALSINDEX); + lua_setfield(L, -2, "__index"); /* mt.__index = _G */ + return 0; +} + + +/* }====================================================== */ + + + +/* auxiliary mark (for internal use) */ +#define AUXMARK "\1" + +static void ICACHE_FLASH_ATTR setpath (lua_State *L, const char *fieldname, const char *envname, + const char *def) { + const char *path = c_getenv(envname); + if (path == NULL) /* no environment variable? */ + lua_pushstring(L, def); /* use default */ + else { + /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */ + path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP, + LUA_PATHSEP AUXMARK LUA_PATHSEP); + luaL_gsub(L, path, AUXMARK, def); + lua_remove(L, -2); + } + setprogdir(L); + lua_setfield(L, -2, fieldname); +} + + +static const luaL_Reg pk_funcs[] = { + {"loadlib", ll_loadlib}, + {"seeall", ll_seeall}, + {NULL, NULL} +}; + + +static const luaL_Reg ll_funcs[] = { + {"module", ll_module}, + {"require", ll_require}, + {NULL, NULL} +}; + + +static const lua_CFunction loaders[] = + {loader_preload, loader_Lua, loader_C, loader_Croot, NULL}; + +#if LUA_OPTIMIZE_MEMORY > 0 +const luaR_entry lmt[] = { + {LRO_STRKEY("__gc"), LRO_FUNCVAL(gctm)}, + {LRO_NILKEY, LRO_NILVAL} +}; +#endif + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_package (lua_State *L) { + int i; + /* create new type _LOADLIB */ +#if LUA_OPTIMIZE_MEMORY == 0 + luaL_newmetatable(L, "_LOADLIB"); + lua_pushlightfunction(L, gctm); + lua_setfield(L, -2, "__gc"); +#else + luaL_rometatable(L, "_LOADLIB", (void*)lmt); +#endif + /* create `package' table */ + luaL_register_light(L, LUA_LOADLIBNAME, pk_funcs); +#if defined(LUA_COMPAT_LOADLIB) + lua_getfield(L, -1, "loadlib"); + lua_setfield(L, LUA_GLOBALSINDEX, "loadlib"); +#endif + lua_pushvalue(L, -1); + lua_replace(L, LUA_ENVIRONINDEX); + /* create `loaders' table */ + lua_createtable(L, sizeof(loaders)/sizeof(loaders[0]) - 1, 0); + /* fill it with pre-defined loaders */ + for (i=0; loaders[i] != NULL; i++) { + lua_pushcfunction(L, loaders[i]); + lua_rawseti(L, -2, i+1); + } + lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */ + setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */ + setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */ + /* store config information */ + lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" + LUA_EXECDIR "\n" LUA_IGMARK); + lua_setfield(L, -2, "config"); + /* set field `loaded' */ + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2); + lua_setfield(L, -2, "loaded"); + /* set field `preload' */ + lua_newtable(L); + lua_setfield(L, -2, "preload"); + lua_pushvalue(L, LUA_GLOBALSINDEX); + luaL_register(L, NULL, ll_funcs); /* open lib into global table */ + lua_pop(L, 1); + return 1; /* return 'package' table */ +} + diff --git a/app/lua/lobject.c b/app/lua/lobject.c new file mode 100644 index 00000000..d8fd8344 --- /dev/null +++ b/app/lua/lobject.c @@ -0,0 +1,216 @@ +/* +** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $ +** Some generic functions over Lua objects +** See Copyright Notice in lua.h +*/ + +#include "c_ctype.h" +#include "c_stdarg.h" +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_string.h" + +#define lobject_c +#define LUA_CORE + +#include "lua.h" + +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "lvm.h" + + + +const TValue luaO_nilobject_ = {LUA_TVALUE_NIL}; + + +/* +** converts an integer to a "floating point byte", represented as +** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if +** eeeee != 0 and (xxx) otherwise. +*/ +int ICACHE_FLASH_ATTR luaO_int2fb (unsigned int x) { + int e = 0; /* expoent */ + while (x >= 16) { + x = (x+1) >> 1; + e++; + } + if (x < 8) return x; + else return ((e+1) << 3) | (cast_int(x) - 8); +} + + +/* converts back */ +int ICACHE_FLASH_ATTR luaO_fb2int (int x) { + int e = (x >> 3) & 31; + if (e == 0) return x; + else return ((x & 7)+8) << (e - 1); +} + + +int ICACHE_FLASH_ATTR luaO_log2 (unsigned int x) { + static const lu_byte log_2[256] = { + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + }; + int l = -1; + while (x >= 256) { l += 8; x >>= 8; } + return l + log_2[x]; + +} + + +int ICACHE_FLASH_ATTR luaO_rawequalObj (const TValue *t1, const TValue *t2) { + if (ttype(t1) != ttype(t2)) return 0; + else switch (ttype(t1)) { + case LUA_TNIL: + return 1; + case LUA_TNUMBER: + return luai_numeq(nvalue(t1), nvalue(t2)); + case LUA_TBOOLEAN: + return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */ + case LUA_TLIGHTUSERDATA: + case LUA_TROTABLE: + case LUA_TLIGHTFUNCTION: + return pvalue(t1) == pvalue(t2); + default: + lua_assert(iscollectable(t1)); + return gcvalue(t1) == gcvalue(t2); + } +} + + +int ICACHE_FLASH_ATTR luaO_str2d (const char *s, lua_Number *result) { + char *endptr; + *result = lua_str2number(s, &endptr); + if (endptr == s) return 0; /* conversion failed */ + if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */ + *result = cast_num(c_strtoul(s, &endptr, 16)); + if (*endptr == '\0') return 1; /* most common case */ + while (isspace(cast(unsigned char, *endptr))) endptr++; + if (*endptr != '\0') return 0; /* invalid trailing characters? */ + return 1; +} + + + +static void ICACHE_FLASH_ATTR pushstr (lua_State *L, const char *str) { + setsvalue2s(L, L->top, luaS_new(L, str)); + incr_top(L); +} + + +/* this function handles only `%d', `%c', %f, %p, and `%s' formats */ +const char *ICACHE_FLASH_ATTR luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { + int n = 1; + pushstr(L, ""); + for (;;) { + const char *e = c_strchr(fmt, '%'); + if (e == NULL) break; + setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt)); + incr_top(L); + switch (*(e+1)) { + case 's': { + const char *s = va_arg(argp, char *); + if (s == NULL) s = "(null)"; + pushstr(L, s); + break; + } + case 'c': { + char buff[2]; + buff[0] = cast(char, va_arg(argp, int)); + buff[1] = '\0'; + pushstr(L, buff); + break; + } + case 'd': { + setnvalue(L->top, cast_num(va_arg(argp, int))); + incr_top(L); + break; + } + case 'f': { + setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber))); + incr_top(L); + break; + } + case 'p': { + char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */ + c_sprintf(buff, "%p", va_arg(argp, void *)); + pushstr(L, buff); + break; + } + case '%': { + pushstr(L, "%"); + break; + } + default: { + char buff[3]; + buff[0] = '%'; + buff[1] = *(e+1); + buff[2] = '\0'; + pushstr(L, buff); + break; + } + } + n += 2; + fmt = e+2; + } + pushstr(L, fmt); + luaV_concat(L, n+1, cast_int(L->top - L->base) - 1); + L->top -= n; + return svalue(L->top - 1); +} + + +const char *ICACHE_FLASH_ATTR luaO_pushfstring (lua_State *L, const char *fmt, ...) { + const char *msg; + va_list argp; + va_start(argp, fmt); + msg = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + return msg; +} + + +void ICACHE_FLASH_ATTR luaO_chunkid (char *out, const char *source, size_t bufflen) { + if (*source == '=') { + c_strncpy(out, source+1, bufflen); /* remove first char */ + out[bufflen-1] = '\0'; /* ensures null termination */ + } + else { /* out = "source", or "...source" */ + if (*source == '@') { + size_t l; + source++; /* skip the `@' */ + bufflen -= sizeof(" '...' "); + l = c_strlen(source); + c_strcpy(out, ""); + if (l > bufflen) { + source += (l-bufflen); /* get last part of file name */ + c_strcat(out, "..."); + } + c_strcat(out, source); + } + else { /* out = [string "string"] */ + size_t len = c_strcspn(source, "\n\r"); /* stop at first newline */ + bufflen -= sizeof(" [string \"...\"] "); + if (len > bufflen) len = bufflen; + c_strcpy(out, "[string \""); + if (source[len] != '\0') { /* must truncate? */ + c_strncat(out, source, len); + c_strcat(out, "..."); + } + else + c_strcat(out, source); + c_strcat(out, "\"]"); + } + } +} diff --git a/app/lua/lobject.h b/app/lua/lobject.h new file mode 100644 index 00000000..0ebc5abf --- /dev/null +++ b/app/lua/lobject.h @@ -0,0 +1,551 @@ +/* +** $Id: lobject.h,v 2.20.1.2 2008/08/06 13:29:48 roberto Exp $ +** Type definitions for Lua objects +** See Copyright Notice in lua.h +*/ + + +#ifndef lobject_h +#define lobject_h + + +#include "c_stdarg.h" + + +#include "llimits.h" +#include "lua.h" + + +/* tags for values visible from Lua */ +#define LAST_TAG LUA_TTHREAD + +#define NUM_TAGS (LAST_TAG+1) + +/* mask for 'read-only' objects. must match READONLYBIT in lgc.h' */ +#define READONLYMASK 128 + + +/* +** Extra tags for non-values +*/ +#define LUA_TPROTO (LAST_TAG+1) +#define LUA_TUPVAL (LAST_TAG+2) +#define LUA_TDEADKEY (LAST_TAG+3) + + +/* +** Union of all collectable objects +*/ +typedef union GCObject GCObject; + + +/* +** Common Header for all collectable objects (in macro form, to be +** included in other objects) +*/ +#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked + + +/* +** Common header in struct form +*/ +typedef struct GCheader { + CommonHeader; +} GCheader; + + + + +/* +** Union of all Lua values +*/ +#if defined( LUA_PACK_VALUE ) && defined( ELUA_ENDIAN_BIG ) +typedef union { + struct { + int _pad0; + GCObject *gc; + }; + struct { + int _pad1; + void *p; + }; + lua_Number n; + struct { + int _pad2; + int b; + }; +} Value; +#else // #if defined( LUA_PACK_VALUE ) && defined( ELUA_ENDIAN_BIG ) +typedef union { + GCObject *gc; + void *p; + lua_Number n; + int b; +} Value; +#endif // #if defined( LUA_PACK_VALUE ) && defined( ELUA_ENDIAN_BIG ) + +/* +** Tagged Values +*/ + +#ifndef LUA_PACK_VALUE +#define TValuefields Value value; int tt +#define LUA_TVALUE_NIL {NULL}, LUA_TNIL + +typedef struct lua_TValue { + TValuefields; +} TValue; +#else // #ifndef LUA_PACK_VALUE +#ifdef ELUA_ENDIAN_LITTLE +#define TValuefields union { \ + struct { \ + int _pad0; \ + int tt_sig; \ + } _ts; \ + struct { \ + int _pad; \ + short tt; \ + short sig; \ + } _t; \ + Value value; \ +} +#define LUA_TVALUE_NIL {0, add_sig(LUA_TNIL)} +#else // #ifdef ELUA_ENDIAN_LITTLE +#define TValuefields union { \ + struct { \ + int tt_sig; \ + int _pad0; \ + } _ts; \ + struct { \ + short sig; \ + short tt; \ + int _pad; \ + } _t; \ + Value value; \ +} +#define LUA_TVALUE_NIL {add_sig(LUA_TNIL), 0} +#endif // #ifdef ELUA_ENDIAN_LITTLE +#define LUA_NOTNUMBER_SIG (-1) +#define add_sig(tt) ( 0xffff0000 | (tt) ) + +typedef TValuefields TValue; +#endif // #ifndef LUA_PACK_VALUE + +/* Macros to test type */ +#ifndef LUA_PACK_VALUE +#define ttisnil(o) (ttype(o) == LUA_TNIL) +#define ttisnumber(o) (ttype(o) == LUA_TNUMBER) +#define ttisstring(o) (ttype(o) == LUA_TSTRING) +#define ttistable(o) (ttype(o) == LUA_TTABLE) +#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION) +#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN) +#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA) +#define ttisthread(o) (ttype(o) == LUA_TTHREAD) +#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA) +#define ttisrotable(o) (ttype(o) == LUA_TROTABLE) +#define ttislightfunction(o) (ttype(o) == LUA_TLIGHTFUNCTION) +#else // #ifndef LUA_PACK_VALUE +#define ttisnil(o) (ttype_sig(o) == add_sig(LUA_TNIL)) +#define ttisnumber(o) ((o)->_t.sig != LUA_NOTNUMBER_SIG) +#define ttisstring(o) (ttype_sig(o) == add_sig(LUA_TSTRING)) +#define ttistable(o) (ttype_sig(o) == add_sig(LUA_TTABLE)) +#define ttisfunction(o) (ttype_sig(o) == add_sig(LUA_TFUNCTION)) +#define ttisboolean(o) (ttype_sig(o) == add_sig(LUA_TBOOLEAN)) +#define ttisuserdata(o) (ttype_sig(o) == add_sig(LUA_TUSERDATA)) +#define ttisthread(o) (ttype_sig(o) == add_sig(LUA_TTHREAD)) +#define ttislightuserdata(o) (ttype_sig(o) == add_sig(LUA_TLIGHTUSERDATA)) +#define ttisrotable(o) (ttype_sig(o) == add_sig(LUA_TROTABLE)) +#define ttislightfunction(o) (ttype_sig(o) == add_sig(LUA_TLIGHTFUNCTION)) +#endif // #ifndef LUA_PACK_VALUE + +/* Macros to access values */ +#ifndef LUA_PACK_VALUE +#define ttype(o) ((o)->tt) +#else // #ifndef LUA_PACK_VALUE +#define ttype(o) ((o)->_t.sig == LUA_NOTNUMBER_SIG ? (o)->_t.tt : LUA_TNUMBER) +#define ttype_sig(o) ((o)->_ts.tt_sig) +#endif // #ifndef LUA_PACK_VALUE +#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc) +#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p) +#define rvalue(o) check_exp(ttisrotable(o), (o)->value.p) +#define fvalue(o) check_exp(ttislightfunction(o), (o)->value.p) +#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n) +#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts) +#define tsvalue(o) (&rawtsvalue(o)->tsv) +#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u) +#define uvalue(o) (&rawuvalue(o)->uv) +#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl) +#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h) +#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b) +#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th) + +#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0)) + +/* +** for internal debug only +*/ +#ifndef LUA_PACK_VALUE +#define checkconsistency(obj) \ + lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt)) + +#define checkliveness(g,obj) \ + lua_assert(!iscollectable(obj) || \ + ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc))) +#else // #ifndef LUA_PACK_VALUE +#define checkconsistency(obj) \ + lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch._t.tt)) + +#define checkliveness(g,obj) \ + lua_assert(!iscollectable(obj) || \ + ((ttype(obj) == (obj)->value.gc->gch._t.tt) && !isdead(g, (obj)->value.gc))) +#endif // #ifndef LUA_PACK_VALUE + +/* Macros to set values */ +#ifndef LUA_PACK_VALUE +#define setnilvalue(obj) ((obj)->tt=LUA_TNIL) + +#define setnvalue(obj,x) \ + { lua_Number i_x = (x); TValue *i_o=(obj); i_o->value.n=i_x; i_o->tt=LUA_TNUMBER; } + +#define setpvalue(obj,x) \ + { void *i_x = (x); TValue *i_o=(obj); i_o->value.p=i_x; i_o->tt=LUA_TLIGHTUSERDATA; } + +#define setrvalue(obj,x) \ + { void *i_x = (x); TValue *i_o=(obj); i_o->value.p=i_x; i_o->tt=LUA_TROTABLE; } + +#define setfvalue(obj,x) \ + { void *i_x = (x); TValue *i_o=(obj); i_o->value.p=i_x; i_o->tt=LUA_TLIGHTFUNCTION; } + +#define setbvalue(obj,x) \ + { int i_x = (x); TValue *i_o=(obj); i_o->value.b=i_x; i_o->tt=LUA_TBOOLEAN; } + +#define setsvalue(L,obj,x) \ + { GCObject *i_x = cast(GCObject *, (x)); \ + TValue *i_o=(obj); \ + i_o->value.gc=i_x; i_o->tt=LUA_TSTRING; \ + checkliveness(G(L),i_o); } + +#define setuvalue(L,obj,x) \ + { GCObject *i_x = cast(GCObject *, (x)); \ + TValue *i_o=(obj); \ + i_o->value.gc=i_x; i_o->tt=LUA_TUSERDATA; \ + checkliveness(G(L),i_o); } + +#define setthvalue(L,obj,x) \ + { GCObject *i_x = cast(GCObject *, (x)); \ + TValue *i_o=(obj); \ + i_o->value.gc=i_x; i_o->tt=LUA_TTHREAD; \ + checkliveness(G(L),i_o); } + +#define setclvalue(L,obj,x) \ + { GCObject *i_x = cast(GCObject *, (x)); \ + TValue *i_o=(obj); \ + i_o->value.gc=i_x; i_o->tt=LUA_TFUNCTION; \ + checkliveness(G(L),i_o); } + +#define sethvalue(L,obj,x) \ + { GCObject *i_x = cast(GCObject *, (x)); \ + TValue *i_o=(obj); \ + i_o->value.gc=i_x; i_o->tt=LUA_TTABLE; \ + checkliveness(G(L),i_o); } + +#define setptvalue(L,obj,x) \ + { GCObject *i_x = cast(GCObject *, (x)); \ + TValue *i_o=(obj); \ + i_o->value.gc=i_x; i_o->tt=LUA_TPROTO; \ + checkliveness(G(L),i_o); } + + + + +#define setobj(L,obj1,obj2) \ + { const TValue *o2=(obj2); TValue *o1=(obj1); \ + o1->value = o2->value; o1->tt=o2->tt; \ + checkliveness(G(L),o1); } +#else // #ifndef LUA_PACK_VALUE +#define setnilvalue(obj) ( ttype_sig(obj) = add_sig(LUA_TNIL) ) + +#define setnvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.n=(x); } + +#define setpvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.p=(x); i_o->_ts.tt_sig=add_sig(LUA_TLIGHTUSERDATA);} + +#define setrvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.p=(x); i_o->_ts.tt_sig=add_sig(LUA_TROTABLE);} + +#define setfvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.p=(x); i_o->_ts.tt_sig=add_sig(LUA_TLIGHTFUNCTION);} + +#define setbvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.b=(x); i_o->_ts.tt_sig=add_sig(LUA_TBOOLEAN);} + +#define setsvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->_ts.tt_sig=add_sig(LUA_TSTRING); \ + checkliveness(G(L),i_o); } + +#define setuvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->_ts.tt_sig=add_sig(LUA_TUSERDATA); \ + checkliveness(G(L),i_o); } + +#define setthvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->_ts.tt_sig=add_sig(LUA_TTHREAD); \ + checkliveness(G(L),i_o); } + +#define setclvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->_ts.tt_sig=add_sig(LUA_TFUNCTION); \ + checkliveness(G(L),i_o); } + +#define sethvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->_ts.tt_sig=add_sig(LUA_TTABLE); \ + checkliveness(G(L),i_o); } + +#define setptvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->_ts.tt_sig=add_sig(LUA_TPROTO); \ + checkliveness(G(L),i_o); } + + + + +#define setobj(L,obj1,obj2) \ + { const TValue *o2=(obj2); TValue *o1=(obj1); \ + o1->value = o2->value; \ + checkliveness(G(L),o1); } +#endif // #ifndef LUA_PACK_VALUE + +/* +** different types of sets, according to destination +*/ + +/* from stack to (same) stack */ +#define setobjs2s setobj +/* to stack (not from same stack) */ +#define setobj2s setobj +#define setsvalue2s setsvalue +#define sethvalue2s sethvalue +#define setptvalue2s setptvalue +/* from table to same table */ +#define setobjt2t setobj +/* to table */ +#define setobj2t setobj +/* to new object */ +#define setobj2n setobj +#define setsvalue2n setsvalue + +#ifndef LUA_PACK_VALUE +#define setttype(obj, tt) (ttype(obj) = (tt)) +#else // #ifndef LUA_PACK_VALUE +/* considering it used only in lgc to set LUA_TDEADKEY */ +/* we could define it this way */ +#define setttype(obj, _tt) ( ttype_sig(obj) = add_sig(_tt) ) +#endif // #ifndef LUA_PACK_VALUE + +#define iscollectable(o) (ttype(o) >= LUA_TSTRING) + + + +typedef TValue *StkId; /* index to stack elements */ + + +/* +** String headers for string table +*/ +typedef union TString { + L_Umaxalign dummy; /* ensures maximum alignment for strings */ + struct { + CommonHeader; + unsigned int hash; + size_t len; + } tsv; +} TString; + + +#define getstr(ts) (((ts)->tsv.marked & READONLYMASK) ? cast(const char *, *(const char**)((ts) + 1)) : cast(const char *, (ts) + 1)) +#define svalue(o) getstr(rawtsvalue(o)) + + + +typedef union Udata { + L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */ + struct { + CommonHeader; + struct Table *metatable; + struct Table *env; + size_t len; + } uv; +} Udata; + + + + +/* +** Function Prototypes +*/ +typedef struct Proto { + CommonHeader; + TValue *k; /* constants used by the function */ + Instruction *code; + struct Proto **p; /* functions defined inside the function */ + int *lineinfo; /* map from opcodes to source lines */ + struct LocVar *locvars; /* information about local variables */ + TString **upvalues; /* upvalue names */ + TString *source; + int sizeupvalues; + int sizek; /* size of `k' */ + int sizecode; + int sizelineinfo; + int sizep; /* size of `p' */ + int sizelocvars; + int linedefined; + int lastlinedefined; + GCObject *gclist; + lu_byte nups; /* number of upvalues */ + lu_byte numparams; + lu_byte is_vararg; + lu_byte maxstacksize; +} Proto; + + +/* masks for new-style vararg */ +#define VARARG_HASARG 1 +#define VARARG_ISVARARG 2 +#define VARARG_NEEDSARG 4 + + +typedef struct LocVar { + TString *varname; + int startpc; /* first point where variable is active */ + int endpc; /* first point where variable is dead */ +} LocVar; + + + +/* +** Upvalues +*/ + +typedef struct UpVal { + CommonHeader; + TValue *v; /* points to stack or to its own value */ + union { + TValue value; /* the value (when closed) */ + struct { /* double linked list (when open) */ + struct UpVal *prev; + struct UpVal *next; + } l; + } u; +} UpVal; + + +/* +** Closures +*/ + +#define ClosureHeader \ + CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \ + struct Table *env + +typedef struct CClosure { + ClosureHeader; + lua_CFunction f; + TValue upvalue[1]; +} CClosure; + + +typedef struct LClosure { + ClosureHeader; + struct Proto *p; + UpVal *upvals[1]; +} LClosure; + + +typedef union Closure { + CClosure c; + LClosure l; +} Closure; + + +#define iscfunction(o) ((ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC)||(ttype(o)==LUA_TLIGHTFUNCTION)) +#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC) + + +/* +** Tables +*/ + +#ifndef LUA_PACK_VALUE +typedef union TKey { + struct { + TValuefields; + struct Node *next; /* for chaining */ + } nk; + TValue tvk; +} TKey; + +#define LUA_TKEY_NIL {LUA_TVALUE_NIL, NULL} +#else // #ifndef LUA_PACK_VALUE +typedef struct TKey { + TValue tvk; + struct { + struct Node *next; /* for chaining */ + } nk; +} TKey; + +#define LUA_TKEY_NIL {LUA_TVALUE_NIL}, {NULL} +#endif // #ifndef LUA_PACK_VALUE + +typedef struct Node { + TValue i_val; + TKey i_key; +} Node; + + +typedef struct Table { + CommonHeader; + lu_byte flags; /* 1<

lsizenode)) + + +#define luaO_nilobject (&luaO_nilobject_) + +LUAI_DATA const TValue luaO_nilobject_; + +#define ceillog2(x) (luaO_log2((x)-1) + 1) + +LUAI_FUNC int luaO_log2 (unsigned int x); +LUAI_FUNC int luaO_int2fb (unsigned int x); +LUAI_FUNC int luaO_fb2int (int x); +LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2); +LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result); +LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, + va_list argp); +LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); +LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len); + + +#endif + diff --git a/app/lua/lopcodes.c b/app/lua/lopcodes.c new file mode 100644 index 00000000..4cc74523 --- /dev/null +++ b/app/lua/lopcodes.c @@ -0,0 +1,102 @@ +/* +** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $ +** See Copyright Notice in lua.h +*/ + + +#define lopcodes_c +#define LUA_CORE + + +#include "lopcodes.h" + + +/* ORDER OP */ + +const char *const luaP_opnames[NUM_OPCODES+1] = { + "MOVE", + "LOADK", + "LOADBOOL", + "LOADNIL", + "GETUPVAL", + "GETGLOBAL", + "GETTABLE", + "SETGLOBAL", + "SETUPVAL", + "SETTABLE", + "NEWTABLE", + "SELF", + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "POW", + "UNM", + "NOT", + "LEN", + "CONCAT", + "JMP", + "EQ", + "LT", + "LE", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "FORLOOP", + "FORPREP", + "TFORLOOP", + "SETLIST", + "CLOSE", + "CLOSURE", + "VARARG", + NULL +}; + + +#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m)) + +const lu_byte luaP_opmodes[NUM_OPCODES] = { +/* T A B C mode opcode */ + opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */ + ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */ + ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */ + ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */ + ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */ + ,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */ + ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */ + ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */ + ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ + ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ + ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ + ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TEST */ + ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */ + ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */ + ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */ + ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */ + ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TFORLOOP */ + ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */ + ,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */ + ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */ + ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */ +}; + diff --git a/app/lua/lopcodes.h b/app/lua/lopcodes.h new file mode 100644 index 00000000..41224d6e --- /dev/null +++ b/app/lua/lopcodes.h @@ -0,0 +1,268 @@ +/* +** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $ +** Opcodes for Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lopcodes_h +#define lopcodes_h + +#include "llimits.h" + + +/*=========================================================================== + We assume that instructions are unsigned numbers. + All instructions have an opcode in the first 6 bits. + Instructions can have the following fields: + `A' : 8 bits + `B' : 9 bits + `C' : 9 bits + `Bx' : 18 bits (`B' and `C' together) + `sBx' : signed Bx + + A signed argument is represented in excess K; that is, the number + value is the unsigned value minus K. K is exactly the maximum value + for that argument (so that -max is represented by 0, and +max is + represented by 2*max), which is half the maximum for the corresponding + unsigned argument. +===========================================================================*/ + + +enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */ + + +/* +** size and position of opcode arguments. +*/ +#define SIZE_C 9 +#define SIZE_B 9 +#define SIZE_Bx (SIZE_C + SIZE_B) +#define SIZE_A 8 + +#define SIZE_OP 6 + +#define POS_OP 0 +#define POS_A (POS_OP + SIZE_OP) +#define POS_C (POS_A + SIZE_A) +#define POS_B (POS_C + SIZE_C) +#define POS_Bx POS_C + + +/* +** limits for opcode arguments. +** we use (signed) int to manipulate most arguments, +** so they must fit in LUAI_BITSINT-1 bits (-1 for sign) +*/ +#if SIZE_Bx < LUAI_BITSINT-1 +#define MAXARG_Bx ((1<>1) /* `sBx' is signed */ +#else +#define MAXARG_Bx MAX_INT +#define MAXARG_sBx MAX_INT +#endif + + +#define MAXARG_A ((1<>POS_OP) & MASK1(SIZE_OP,0))) +#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ + ((cast(Instruction, o)<>POS_A) & MASK1(SIZE_A,0))) +#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \ + ((cast(Instruction, u)<>POS_B) & MASK1(SIZE_B,0))) +#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \ + ((cast(Instruction, b)<>POS_C) & MASK1(SIZE_C,0))) +#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \ + ((cast(Instruction, b)<>POS_Bx) & MASK1(SIZE_Bx,0))) +#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \ + ((cast(Instruction, b)< C) then pc++ */ +OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ + +OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ +OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ +OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */ + +OP_FORLOOP,/* A sBx R(A)+=R(A+2); + if R(A) =) R(A)*/ +OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ + +OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ +} OpCode; + + +#define NUM_OPCODES (cast(int, OP_VARARG) + 1) + + + +/*=========================================================================== + Notes: + (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, + and can be 0: OP_CALL then sets `top' to last_result+1, so + next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'. + + (*) In OP_VARARG, if (B == 0) then use actual number of varargs and + set top (like in OP_CALL with C == 0). + + (*) In OP_RETURN, if (B == 0) then return up to `top' + + (*) In OP_SETLIST, if (B == 0) then B = `top'; + if (C == 0) then next `instruction' is real C + + (*) For comparisons, A specifies what condition the test should accept + (true or false). + + (*) All `skips' (pc++) assume that next instruction is a jump +===========================================================================*/ + + +/* +** masks for instruction properties. The format is: +** bits 0-1: op mode +** bits 2-3: C arg mode +** bits 4-5: B arg mode +** bit 6: instruction set register A +** bit 7: operator is a test +*/ + +enum OpArgMask { + OpArgN, /* argument is not used */ + OpArgU, /* argument is used */ + OpArgR, /* argument is a register or a jump offset */ + OpArgK /* argument is a constant or register/constant */ +}; + +LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES]; + +#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3)) +#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3)) +#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3)) +#define testAMode(m) (luaP_opmodes[m] & (1 << 6)) +#define testTMode(m) (luaP_opmodes[m] & (1 << 7)) + + +LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */ + + +/* number of list items to accumulate before a SETLIST instruction */ +#define LFIELDS_PER_FLUSH 50 + + +#endif diff --git a/app/lua/lparser.c b/app/lua/lparser.c new file mode 100644 index 00000000..98c810a2 --- /dev/null +++ b/app/lua/lparser.c @@ -0,0 +1,1345 @@ +/* +** $Id: lparser.c,v 2.42.1.3 2007/12/28 15:32:23 roberto Exp $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + + +#include "c_string.h" + +#define lparser_c +#define LUA_CORE + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" + + + +#define hasmultret(k) ((k) == VCALL || (k) == VVARARG) + +#define getlocvar(fs, i) ((fs)->f->locvars[(fs)->actvar[i]]) + +#define luaY_checklimit(fs,v,l,m) if ((v)>(l)) errorlimit(fs,l,m) + + +/* +** nodes for block list (list of active blocks) +*/ +typedef struct BlockCnt { + struct BlockCnt *previous; /* chain */ + int breaklist; /* list of jumps out of this loop */ + lu_byte nactvar; /* # active locals outside the breakable structure */ + lu_byte upval; /* true if some variable in the block is an upvalue */ + lu_byte isbreakable; /* true if `block' is a loop */ +} BlockCnt; + + + +/* +** prototypes for recursive non-terminal functions +*/ +static void chunk (LexState *ls); +static void expr (LexState *ls, expdesc *v); + + +static void ICACHE_FLASH_ATTR anchor_token (LexState *ls) { + if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) { + TString *ts = ls->t.seminfo.ts; + luaX_newstring(ls, getstr(ts), ts->tsv.len); + } +} + + +static void ICACHE_FLASH_ATTR error_expected (LexState *ls, int token) { + luaX_syntaxerror(ls, + luaO_pushfstring(ls->L, LUA_QS " expected", luaX_token2str(ls, token))); +} + + +static void ICACHE_FLASH_ATTR errorlimit (FuncState *fs, int limit, const char *what) { + const char *msg = (fs->f->linedefined == 0) ? + luaO_pushfstring(fs->L, "main function has more than %d %s", limit, what) : + luaO_pushfstring(fs->L, "function at line %d has more than %d %s", + fs->f->linedefined, limit, what); + luaX_lexerror(fs->ls, msg, 0); +} + + +static int ICACHE_FLASH_ATTR testnext (LexState *ls, int c) { + if (ls->t.token == c) { + luaX_next(ls); + return 1; + } + else return 0; +} + + +static void ICACHE_FLASH_ATTR check (LexState *ls, int c) { + if (ls->t.token != c) + error_expected(ls, c); +} + +static void ICACHE_FLASH_ATTR checknext (LexState *ls, int c) { + check(ls, c); + luaX_next(ls); +} + + +#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } + + + +static void ICACHE_FLASH_ATTR check_match (LexState *ls, int what, int who, int where) { + if (!testnext(ls, what)) { + if (where == ls->linenumber) + error_expected(ls, what); + else { + luaX_syntaxerror(ls, luaO_pushfstring(ls->L, + LUA_QS " expected (to close " LUA_QS " at line %d)", + luaX_token2str(ls, what), luaX_token2str(ls, who), where)); + } + } +} + + +static TString *ICACHE_FLASH_ATTR str_checkname (LexState *ls) { + TString *ts; + check(ls, TK_NAME); + ts = ls->t.seminfo.ts; + luaX_next(ls); + return ts; +} + + +static void ICACHE_FLASH_ATTR init_exp (expdesc *e, expkind k, int i) { + e->f = e->t = NO_JUMP; + e->k = k; + e->u.s.info = i; +} + + +static void ICACHE_FLASH_ATTR codestring (LexState *ls, expdesc *e, TString *s) { + init_exp(e, VK, luaK_stringK(ls->fs, s)); +} + + +static void ICACHE_FLASH_ATTR checkname(LexState *ls, expdesc *e) { + codestring(ls, e, str_checkname(ls)); +} + + +static int ICACHE_FLASH_ATTR registerlocalvar (LexState *ls, TString *varname) { + FuncState *fs = ls->fs; + Proto *f = fs->f; + int oldsize = f->sizelocvars; + luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, + LocVar, SHRT_MAX, "too many local variables"); + while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL; + f->locvars[fs->nlocvars].varname = varname; + luaC_objbarrier(ls->L, f, varname); + return fs->nlocvars++; +} + + +#define new_localvarliteral(ls,v,n) \ + new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n) + + +static void ICACHE_FLASH_ATTR new_localvar (LexState *ls, TString *name, int n) { + FuncState *fs = ls->fs; + luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, "local variables"); + fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name)); +} + + +static void ICACHE_FLASH_ATTR adjustlocalvars (LexState *ls, int nvars) { + FuncState *fs = ls->fs; + fs->nactvar = cast_byte(fs->nactvar + nvars); + for (; nvars; nvars--) { + getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc; + } +} + + +static void ICACHE_FLASH_ATTR removevars (LexState *ls, int tolevel) { + FuncState *fs = ls->fs; + while (fs->nactvar > tolevel) + getlocvar(fs, --fs->nactvar).endpc = fs->pc; +} + + +static int ICACHE_FLASH_ATTR indexupvalue (FuncState *fs, TString *name, expdesc *v) { + int i; + Proto *f = fs->f; + int oldsize = f->sizeupvalues; + for (i=0; inups; i++) { + if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) { + lua_assert(f->upvalues[i] == name); + return i; + } + } + /* new one */ + luaY_checklimit(fs, f->nups + 1, LUAI_MAXUPVALUES, "upvalues"); + luaM_growvector(fs->L, f->upvalues, f->nups, f->sizeupvalues, + TString *, MAX_INT, ""); + while (oldsize < f->sizeupvalues) f->upvalues[oldsize++] = NULL; + f->upvalues[f->nups] = name; + luaC_objbarrier(fs->L, f, name); + lua_assert(v->k == VLOCAL || v->k == VUPVAL); + fs->upvalues[f->nups].k = cast_byte(v->k); + fs->upvalues[f->nups].info = cast_byte(v->u.s.info); + return f->nups++; +} + + +static int ICACHE_FLASH_ATTR searchvar (FuncState *fs, TString *n) { + int i; + for (i=fs->nactvar-1; i >= 0; i--) { + if (n == getlocvar(fs, i).varname) + return i; + } + return -1; /* not found */ +} + + +static void ICACHE_FLASH_ATTR markupval (FuncState *fs, int level) { + BlockCnt *bl = fs->bl; + while (bl && bl->nactvar > level) bl = bl->previous; + if (bl) bl->upval = 1; +} + + +static int ICACHE_FLASH_ATTR singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { + if (fs == NULL) { /* no more levels? */ + init_exp(var, VGLOBAL, NO_REG); /* default is global variable */ + return VGLOBAL; + } + else { + int v = searchvar(fs, n); /* look up at current level */ + if (v >= 0) { + init_exp(var, VLOCAL, v); + if (!base) + markupval(fs, v); /* local will be used as an upval */ + return VLOCAL; + } + else { /* not found at current level; try upper one */ + if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL) + return VGLOBAL; + var->u.s.info = indexupvalue(fs, n, var); /* else was LOCAL or UPVAL */ + var->k = VUPVAL; /* upvalue in this level */ + return VUPVAL; + } + } +} + + +static void ICACHE_FLASH_ATTR singlevar (LexState *ls, expdesc *var) { + TString *varname = str_checkname(ls); + FuncState *fs = ls->fs; + if (singlevaraux(fs, varname, var, 1) == VGLOBAL) + var->u.s.info = luaK_stringK(fs, varname); /* info points to global name */ +} + + +static void ICACHE_FLASH_ATTR adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { + FuncState *fs = ls->fs; + int extra = nvars - nexps; + if (hasmultret(e->k)) { + extra++; /* includes call itself */ + if (extra < 0) extra = 0; + luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ + if (extra > 1) luaK_reserveregs(fs, extra-1); + } + else { + if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */ + if (extra > 0) { + int reg = fs->freereg; + luaK_reserveregs(fs, extra); + luaK_nil(fs, reg, extra); + } + } +} + + +static void ICACHE_FLASH_ATTR enterlevel (LexState *ls) { + if (++ls->L->nCcalls > LUAI_MAXCCALLS) + luaX_lexerror(ls, "chunk has too many syntax levels", 0); +} + + +#define leavelevel(ls) ((ls)->L->nCcalls--) + + +static void ICACHE_FLASH_ATTR enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) { + bl->breaklist = NO_JUMP; + bl->isbreakable = isbreakable; + bl->nactvar = fs->nactvar; + bl->upval = 0; + bl->previous = fs->bl; + fs->bl = bl; + lua_assert(fs->freereg == fs->nactvar); +} + + +static void ICACHE_FLASH_ATTR leaveblock (FuncState *fs) { + BlockCnt *bl = fs->bl; + fs->bl = bl->previous; + removevars(fs->ls, bl->nactvar); + if (bl->upval) + luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); + /* a block either controls scope or breaks (never both) */ + lua_assert(!bl->isbreakable || !bl->upval); + lua_assert(bl->nactvar == fs->nactvar); + fs->freereg = fs->nactvar; /* free registers */ + luaK_patchtohere(fs, bl->breaklist); +} + + +static void ICACHE_FLASH_ATTR pushclosure (LexState *ls, FuncState *func, expdesc *v) { + FuncState *fs = ls->fs; + Proto *f = fs->f; + int oldsize = f->sizep; + int i; + luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *, + MAXARG_Bx, "constant table overflow"); + while (oldsize < f->sizep) f->p[oldsize++] = NULL; + f->p[fs->np++] = func->f; + luaC_objbarrier(ls->L, f, func->f); + init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1)); + for (i=0; if->nups; i++) { + OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; + luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0); + } +} + + +static void ICACHE_FLASH_ATTR open_func (LexState *ls, FuncState *fs) { + lua_State *L = ls->L; + Proto *f = luaF_newproto(L); + fs->f = f; + fs->prev = ls->fs; /* linked list of funcstates */ + fs->ls = ls; + fs->L = L; + ls->fs = fs; + fs->pc = 0; + fs->lasttarget = -1; + fs->jpc = NO_JUMP; + fs->freereg = 0; + fs->nk = 0; + fs->np = 0; + fs->nlocvars = 0; + fs->nactvar = 0; + fs->bl = NULL; + f->source = ls->source; + f->maxstacksize = 2; /* registers 0/1 are always valid */ + fs->h = luaH_new(L, 0, 0); + /* anchor table of constants and prototype (to avoid being collected) */ + sethvalue2s(L, L->top, fs->h); + incr_top(L); + setptvalue2s(L, L->top, f); + incr_top(L); +} + + +static void ICACHE_FLASH_ATTR close_func (LexState *ls) { + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Proto *f = fs->f; + removevars(ls, 0); + luaK_ret(fs, 0, 0); /* final return */ + luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction); + f->sizecode = fs->pc; + luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int); + f->sizelineinfo = fs->pc; + luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue); + f->sizek = fs->nk; + luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *); + f->sizep = fs->np; + luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar); + f->sizelocvars = fs->nlocvars; + luaM_reallocvector(L, f->upvalues, f->sizeupvalues, f->nups, TString *); + f->sizeupvalues = f->nups; + lua_assert(luaG_checkcode(f)); + lua_assert(fs->bl == NULL); + ls->fs = fs->prev; + /* last token read was anchored in defunct function; must reanchor it */ + if (fs) anchor_token(ls); + L->top -= 2; /* remove table and prototype from the stack */ +} + + +Proto *ICACHE_FLASH_ATTR luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { + struct LexState lexstate; + struct FuncState funcstate; + TString *tname = luaS_new(L, name); + setsvalue2s(L, L->top, tname); /* protect name */ + incr_top(L); + lexstate.buff = buff; + luaX_setinput(L, &lexstate, z, tname); + open_func(&lexstate, &funcstate); + funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */ + luaX_next(&lexstate); /* read first token */ + chunk(&lexstate); + check(&lexstate, TK_EOS); + close_func(&lexstate); + L->top--; /* remove 'name' from stack */ + lua_assert(funcstate.prev == NULL); + lua_assert(funcstate.f->nups == 0); + lua_assert(lexstate.fs == NULL); + return funcstate.f; +} + + + +/*============================================================*/ +/* GRAMMAR RULES */ +/*============================================================*/ + + +static void ICACHE_FLASH_ATTR field (LexState *ls, expdesc *v) { + /* field -> ['.' | ':'] NAME */ + FuncState *fs = ls->fs; + expdesc key; + luaK_exp2anyreg(fs, v); + luaX_next(ls); /* skip the dot or colon */ + checkname(ls, &key); + luaK_indexed(fs, v, &key); +} + + +static void ICACHE_FLASH_ATTR yindex (LexState *ls, expdesc *v) { + /* index -> '[' expr ']' */ + luaX_next(ls); /* skip the '[' */ + expr(ls, v); + luaK_exp2val(ls->fs, v); + checknext(ls, ']'); +} + + +/* +** {====================================================================== +** Rules for Constructors +** ======================================================================= +*/ + + +struct ConsControl { + expdesc v; /* last list item read */ + expdesc *t; /* table descriptor */ + int nh; /* total number of `record' elements */ + int na; /* total number of array elements */ + int tostore; /* number of array elements pending to be stored */ +}; + + +static void ICACHE_FLASH_ATTR recfield (LexState *ls, struct ConsControl *cc) { + /* recfield -> (NAME | `['exp1`]') = exp1 */ + FuncState *fs = ls->fs; + int reg = ls->fs->freereg; + expdesc key, val; + int rkkey; + if (ls->t.token == TK_NAME) { + luaY_checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); + checkname(ls, &key); + } + else /* ls->t.token == '[' */ + yindex(ls, &key); + cc->nh++; + checknext(ls, '='); + rkkey = luaK_exp2RK(fs, &key); + expr(ls, &val); + luaK_codeABC(fs, OP_SETTABLE, cc->t->u.s.info, rkkey, luaK_exp2RK(fs, &val)); + fs->freereg = reg; /* free registers */ +} + + +static void ICACHE_FLASH_ATTR closelistfield (FuncState *fs, struct ConsControl *cc) { + if (cc->v.k == VVOID) return; /* there is no list item */ + luaK_exp2nextreg(fs, &cc->v); + cc->v.k = VVOID; + if (cc->tostore == LFIELDS_PER_FLUSH) { + luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); /* flush */ + cc->tostore = 0; /* no more items pending */ + } +} + + +static void ICACHE_FLASH_ATTR lastlistfield (FuncState *fs, struct ConsControl *cc) { + if (cc->tostore == 0) return; + if (hasmultret(cc->v.k)) { + luaK_setmultret(fs, &cc->v); + luaK_setlist(fs, cc->t->u.s.info, cc->na, LUA_MULTRET); + cc->na--; /* do not count last expression (unknown number of elements) */ + } + else { + if (cc->v.k != VVOID) + luaK_exp2nextreg(fs, &cc->v); + luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); + } +} + + +static void ICACHE_FLASH_ATTR listfield (LexState *ls, struct ConsControl *cc) { + expr(ls, &cc->v); + luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); + cc->na++; + cc->tostore++; +} + + +static void ICACHE_FLASH_ATTR constructor (LexState *ls, expdesc *t) { + /* constructor -> ?? */ + FuncState *fs = ls->fs; + int line = ls->linenumber; + int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); + struct ConsControl cc; + cc.na = cc.nh = cc.tostore = 0; + cc.t = t; + init_exp(t, VRELOCABLE, pc); + init_exp(&cc.v, VVOID, 0); /* no value (yet) */ + luaK_exp2nextreg(ls->fs, t); /* fix it at stack top (for gc) */ + checknext(ls, '{'); + do { + lua_assert(cc.v.k == VVOID || cc.tostore > 0); + if (ls->t.token == '}') break; + closelistfield(fs, &cc); + switch(ls->t.token) { + case TK_NAME: { /* may be listfields or recfields */ + luaX_lookahead(ls); + if (ls->lookahead.token != '=') /* expression? */ + listfield(ls, &cc); + else + recfield(ls, &cc); + break; + } + case '[': { /* constructor_item -> recfield */ + recfield(ls, &cc); + break; + } + default: { /* constructor_part -> listfield */ + listfield(ls, &cc); + break; + } + } + } while (testnext(ls, ',') || testnext(ls, ';')); + check_match(ls, '}', '{', line); + lastlistfield(fs, &cc); + SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ + SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */ +} + +/* }====================================================================== */ + + + +static void ICACHE_FLASH_ATTR parlist (LexState *ls) { + /* parlist -> [ param { `,' param } ] */ + FuncState *fs = ls->fs; + Proto *f = fs->f; + int nparams = 0; + f->is_vararg = 0; + if (ls->t.token != ')') { /* is `parlist' not empty? */ + do { + switch (ls->t.token) { + case TK_NAME: { /* param -> NAME */ + new_localvar(ls, str_checkname(ls), nparams++); + break; + } + case TK_DOTS: { /* param -> `...' */ + luaX_next(ls); +#if defined(LUA_COMPAT_VARARG) + /* use `arg' as default name */ + new_localvarliteral(ls, "arg", nparams++); + f->is_vararg = VARARG_HASARG | VARARG_NEEDSARG; +#endif + f->is_vararg |= VARARG_ISVARARG; + break; + } + default: luaX_syntaxerror(ls, " or " LUA_QL("...") " expected"); + } + } while (!f->is_vararg && testnext(ls, ',')); + } + adjustlocalvars(ls, nparams); + f->numparams = cast_byte(fs->nactvar - (f->is_vararg & VARARG_HASARG)); + luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */ +} + + +static void ICACHE_FLASH_ATTR body (LexState *ls, expdesc *e, int needself, int line) { + /* body -> `(' parlist `)' chunk END */ + FuncState new_fs; + open_func(ls, &new_fs); + new_fs.f->linedefined = line; + checknext(ls, '('); + if (needself) { + new_localvarliteral(ls, "self", 0); + adjustlocalvars(ls, 1); + } + parlist(ls); + checknext(ls, ')'); + chunk(ls); + new_fs.f->lastlinedefined = ls->linenumber; + check_match(ls, TK_END, TK_FUNCTION, line); + close_func(ls); + pushclosure(ls, &new_fs, e); +} + + +static int ICACHE_FLASH_ATTR explist1 (LexState *ls, expdesc *v) { + /* explist1 -> expr { `,' expr } */ + int n = 1; /* at least one expression */ + expr(ls, v); + while (testnext(ls, ',')) { + luaK_exp2nextreg(ls->fs, v); + expr(ls, v); + n++; + } + return n; +} + + +static void ICACHE_FLASH_ATTR funcargs (LexState *ls, expdesc *f) { + FuncState *fs = ls->fs; + expdesc args; + int base, nparams; + int line = ls->linenumber; + switch (ls->t.token) { + case '(': { /* funcargs -> `(' [ explist1 ] `)' */ + if (line != ls->lastline) + luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)"); + luaX_next(ls); + if (ls->t.token == ')') /* arg list is empty? */ + args.k = VVOID; + else { + explist1(ls, &args); + luaK_setmultret(fs, &args); + } + check_match(ls, ')', '(', line); + break; + } + case '{': { /* funcargs -> constructor */ + constructor(ls, &args); + break; + } + case TK_STRING: { /* funcargs -> STRING */ + codestring(ls, &args, ls->t.seminfo.ts); + luaX_next(ls); /* must use `seminfo' before `next' */ + break; + } + default: { + luaX_syntaxerror(ls, "function arguments expected"); + return; + } + } + lua_assert(f->k == VNONRELOC); + base = f->u.s.info; /* base register for call */ + if (hasmultret(args.k)) + nparams = LUA_MULTRET; /* open call */ + else { + if (args.k != VVOID) + luaK_exp2nextreg(fs, &args); /* close last argument */ + nparams = fs->freereg - (base+1); + } + init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); + luaK_fixline(fs, line); + fs->freereg = base+1; /* call remove function and arguments and leaves + (unless changed) one result */ +} + + + + +/* +** {====================================================================== +** Expression parsing +** ======================================================================= +*/ + + +static void ICACHE_FLASH_ATTR prefixexp (LexState *ls, expdesc *v) { + /* prefixexp -> NAME | '(' expr ')' */ + switch (ls->t.token) { + case '(': { + int line = ls->linenumber; + luaX_next(ls); + expr(ls, v); + check_match(ls, ')', '(', line); + luaK_dischargevars(ls->fs, v); + return; + } + case TK_NAME: { + singlevar(ls, v); + return; + } + default: { + luaX_syntaxerror(ls, "unexpected symbol"); + return; + } + } +} + + +static void ICACHE_FLASH_ATTR primaryexp (LexState *ls, expdesc *v) { + /* primaryexp -> + prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */ + FuncState *fs = ls->fs; + prefixexp(ls, v); + for (;;) { + switch (ls->t.token) { + case '.': { /* field */ + field(ls, v); + break; + } + case '[': { /* `[' exp1 `]' */ + expdesc key; + luaK_exp2anyreg(fs, v); + yindex(ls, &key); + luaK_indexed(fs, v, &key); + break; + } + case ':': { /* `:' NAME funcargs */ + expdesc key; + luaX_next(ls); + checkname(ls, &key); + luaK_self(fs, v, &key); + funcargs(ls, v); + break; + } + case '(': case TK_STRING: case '{': { /* funcargs */ + luaK_exp2nextreg(fs, v); + funcargs(ls, v); + break; + } + default: return; + } + } +} + + +static void ICACHE_FLASH_ATTR simpleexp (LexState *ls, expdesc *v) { + /* simpleexp -> NUMBER | STRING | NIL | true | false | ... | + constructor | FUNCTION body | primaryexp */ + switch (ls->t.token) { + case TK_NUMBER: { + init_exp(v, VKNUM, 0); + v->u.nval = ls->t.seminfo.r; + break; + } + case TK_STRING: { + codestring(ls, v, ls->t.seminfo.ts); + break; + } + case TK_NIL: { + init_exp(v, VNIL, 0); + break; + } + case TK_TRUE: { + init_exp(v, VTRUE, 0); + break; + } + case TK_FALSE: { + init_exp(v, VFALSE, 0); + break; + } + case TK_DOTS: { /* vararg */ + FuncState *fs = ls->fs; + check_condition(ls, fs->f->is_vararg, + "cannot use " LUA_QL("...") " outside a vararg function"); + fs->f->is_vararg &= ~VARARG_NEEDSARG; /* don't need 'arg' */ + init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); + break; + } + case '{': { /* constructor */ + constructor(ls, v); + return; + } + case TK_FUNCTION: { + luaX_next(ls); + body(ls, v, 0, ls->linenumber); + return; + } + default: { + primaryexp(ls, v); + return; + } + } + luaX_next(ls); +} + + +static UnOpr ICACHE_FLASH_ATTR getunopr (int op) { + switch (op) { + case TK_NOT: return OPR_NOT; + case '-': return OPR_MINUS; + case '#': return OPR_LEN; + default: return OPR_NOUNOPR; + } +} + + +static BinOpr ICACHE_FLASH_ATTR getbinopr (int op) { + switch (op) { + case '+': return OPR_ADD; + case '-': return OPR_SUB; + case '*': return OPR_MUL; + case '/': return OPR_DIV; + case '%': return OPR_MOD; + case '^': return OPR_POW; + case TK_CONCAT: return OPR_CONCAT; + case TK_NE: return OPR_NE; + case TK_EQ: return OPR_EQ; + case '<': return OPR_LT; + case TK_LE: return OPR_LE; + case '>': return OPR_GT; + case TK_GE: return OPR_GE; + case TK_AND: return OPR_AND; + case TK_OR: return OPR_OR; + default: return OPR_NOBINOPR; + } +} + + +static const struct { + lu_byte left; /* left priority for each binary operator */ + lu_byte right; /* right priority */ +} priority[] = { /* ORDER OPR */ + {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `/' `%' */ + {10, 9}, {5, 4}, /* power and concat (right associative) */ + {3, 3}, {3, 3}, /* equality and inequality */ + {3, 3}, {3, 3}, {3, 3}, {3, 3}, /* order */ + {2, 2}, {1, 1} /* logical (and/or) */ +}; + +#define UNARY_PRIORITY 8 /* priority for unary operators */ + + +/* +** subexpr -> (simpleexp | unop subexpr) { binop subexpr } +** where `binop' is any binary operator with a priority higher than `limit' +*/ +static BinOpr ICACHE_FLASH_ATTR subexpr (LexState *ls, expdesc *v, unsigned int limit) { + BinOpr op; + UnOpr uop; + enterlevel(ls); + uop = getunopr(ls->t.token); + if (uop != OPR_NOUNOPR) { + luaX_next(ls); + subexpr(ls, v, UNARY_PRIORITY); + luaK_prefix(ls->fs, uop, v); + } + else simpleexp(ls, v); + /* expand while operators have priorities higher than `limit' */ + op = getbinopr(ls->t.token); + while (op != OPR_NOBINOPR && priority[op].left > limit) { + expdesc v2; + BinOpr nextop; + luaX_next(ls); + luaK_infix(ls->fs, op, v); + /* read sub-expression with higher priority */ + nextop = subexpr(ls, &v2, priority[op].right); + luaK_posfix(ls->fs, op, v, &v2); + op = nextop; + } + leavelevel(ls); + return op; /* return first untreated operator */ +} + + +static void ICACHE_FLASH_ATTR expr (LexState *ls, expdesc *v) { + subexpr(ls, v, 0); +} + +/* }==================================================================== */ + + + +/* +** {====================================================================== +** Rules for Statements +** ======================================================================= +*/ + + +static int ICACHE_FLASH_ATTR block_follow (int token) { + switch (token) { + case TK_ELSE: case TK_ELSEIF: case TK_END: + case TK_UNTIL: case TK_EOS: + return 1; + default: return 0; + } +} + + +static void ICACHE_FLASH_ATTR block (LexState *ls) { + /* block -> chunk */ + FuncState *fs = ls->fs; + BlockCnt *pbl = (BlockCnt*)luaM_malloc(ls->L,sizeof(BlockCnt)); + enterblock(fs, pbl, 0); + chunk(ls); + lua_assert(pbl->breaklist == NO_JUMP); + leaveblock(fs); + luaM_free(ls->L,pbl); +} + + +/* +** structure to chain all variables in the left-hand side of an +** assignment +*/ +struct LHS_assign { + struct LHS_assign *prev; + expdesc v; /* variable (global, local, upvalue, or indexed) */ +}; + + +/* +** check whether, in an assignment to a local variable, the local variable +** is needed in a previous assignment (to a table). If so, save original +** local value in a safe place and use this safe copy in the previous +** assignment. +*/ +static void ICACHE_FLASH_ATTR check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { + FuncState *fs = ls->fs; + int extra = fs->freereg; /* eventual position to save local variable */ + int conflict = 0; + for (; lh; lh = lh->prev) { + if (lh->v.k == VINDEXED) { + if (lh->v.u.s.info == v->u.s.info) { /* conflict? */ + conflict = 1; + lh->v.u.s.info = extra; /* previous assignment will use safe copy */ + } + if (lh->v.u.s.aux == v->u.s.info) { /* conflict? */ + conflict = 1; + lh->v.u.s.aux = extra; /* previous assignment will use safe copy */ + } + } + } + if (conflict) { + luaK_codeABC(fs, OP_MOVE, fs->freereg, v->u.s.info, 0); /* make copy */ + luaK_reserveregs(fs, 1); + } +} + + +static void ICACHE_FLASH_ATTR assignment (LexState *ls, struct LHS_assign *lh, int nvars) { + expdesc e; + check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED, + "syntax error"); + if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */ + struct LHS_assign nv; + nv.prev = lh; + primaryexp(ls, &nv.v); + if (nv.v.k == VLOCAL) + check_conflict(ls, lh, &nv.v); + luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls, + "variables in assignment"); + assignment(ls, &nv, nvars+1); + } + else { /* assignment -> `=' explist1 */ + int nexps; + checknext(ls, '='); + nexps = explist1(ls, &e); + if (nexps != nvars) { + adjust_assign(ls, nvars, nexps, &e); + if (nexps > nvars) + ls->fs->freereg -= nexps - nvars; /* remove extra values */ + } + else { + luaK_setoneret(ls->fs, &e); /* close last expression */ + luaK_storevar(ls->fs, &lh->v, &e); + return; /* avoid default */ + } + } + init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ + luaK_storevar(ls->fs, &lh->v, &e); +} + + +static int ICACHE_FLASH_ATTR cond (LexState *ls) { + /* cond -> exp */ + expdesc v; + expr(ls, &v); /* read condition */ + if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */ + luaK_goiftrue(ls->fs, &v); + return v.f; +} + + +static void ICACHE_FLASH_ATTR breakstat (LexState *ls) { + FuncState *fs = ls->fs; + BlockCnt *bl = fs->bl; + int upval = 0; + while (bl && !bl->isbreakable) { + upval |= bl->upval; + bl = bl->previous; + } + if (!bl) + luaX_syntaxerror(ls, "no loop to break"); + if (upval) + luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); + luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); +} + + +static void ICACHE_FLASH_ATTR whilestat (LexState *ls, int line) { + /* whilestat -> WHILE cond DO block END */ + FuncState *fs = ls->fs; + int whileinit; + int condexit; + BlockCnt bl; + luaX_next(ls); /* skip WHILE */ + whileinit = luaK_getlabel(fs); + condexit = cond(ls); + enterblock(fs, &bl, 1); + checknext(ls, TK_DO); + block(ls); + luaK_patchlist(fs, luaK_jump(fs), whileinit); + check_match(ls, TK_END, TK_WHILE, line); + leaveblock(fs); + luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ +} + + +static void ICACHE_FLASH_ATTR repeatstat (LexState *ls, int line) { + /* repeatstat -> REPEAT block UNTIL cond */ + int condexit; + FuncState *fs = ls->fs; + int repeat_init = luaK_getlabel(fs); + BlockCnt bl1, bl2; + enterblock(fs, &bl1, 1); /* loop block */ + enterblock(fs, &bl2, 0); /* scope block */ + luaX_next(ls); /* skip REPEAT */ + chunk(ls); + check_match(ls, TK_UNTIL, TK_REPEAT, line); + condexit = cond(ls); /* read condition (inside scope block) */ + if (!bl2.upval) { /* no upvalues? */ + leaveblock(fs); /* finish scope */ + luaK_patchlist(ls->fs, condexit, repeat_init); /* close the loop */ + } + else { /* complete semantics when there are upvalues */ + breakstat(ls); /* if condition then break */ + luaK_patchtohere(ls->fs, condexit); /* else... */ + leaveblock(fs); /* finish scope... */ + luaK_patchlist(ls->fs, luaK_jump(fs), repeat_init); /* and repeat */ + } + leaveblock(fs); /* finish loop */ +} + + +static int ICACHE_FLASH_ATTR exp1 (LexState *ls) { + expdesc e; + int k; + expr(ls, &e); + k = e.k; + luaK_exp2nextreg(ls->fs, &e); + return k; +} + + +static void ICACHE_FLASH_ATTR forbody (LexState *ls, int base, int line, int nvars, int isnum) { + /* forbody -> DO block */ + BlockCnt *pbl = (BlockCnt*)luaM_malloc(ls->L,sizeof(BlockCnt)); + FuncState *fs = ls->fs; + int prep, endfor; + adjustlocalvars(ls, 3); /* control variables */ + checknext(ls, TK_DO); + prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs); + enterblock(fs, pbl, 0); /* scope for declared variables */ + adjustlocalvars(ls, nvars); + luaK_reserveregs(fs, nvars); + block(ls); + leaveblock(fs); /* end of scope for declared variables */ + luaK_patchtohere(fs, prep); + endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) : + luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars); + luaK_fixline(fs, line); /* pretend that `OP_FOR' starts the loop */ + luaK_patchlist(fs, (isnum ? endfor : luaK_jump(fs)), prep + 1); + luaM_free(ls->L,pbl); +} + + +static void ICACHE_FLASH_ATTR fornum (LexState *ls, TString *varname, int line) { + /* fornum -> NAME = exp1,exp1[,exp1] forbody */ + FuncState *fs = ls->fs; + int base = fs->freereg; + new_localvarliteral(ls, "(for index)", 0); + new_localvarliteral(ls, "(for limit)", 1); + new_localvarliteral(ls, "(for step)", 2); + new_localvar(ls, varname, 3); + checknext(ls, '='); + exp1(ls); /* initial value */ + checknext(ls, ','); + exp1(ls); /* limit */ + if (testnext(ls, ',')) + exp1(ls); /* optional step */ + else { /* default step = 1 */ + luaK_codeABx(fs, OP_LOADK, fs->freereg, luaK_numberK(fs, 1)); + luaK_reserveregs(fs, 1); + } + forbody(ls, base, line, 1, 1); +} + + +static void ICACHE_FLASH_ATTR forlist (LexState *ls, TString *indexname) { + /* forlist -> NAME {,NAME} IN explist1 forbody */ + FuncState *fs = ls->fs; + expdesc e; + int nvars = 0; + int line; + int base = fs->freereg; + /* create control variables */ + new_localvarliteral(ls, "(for generator)", nvars++); + new_localvarliteral(ls, "(for state)", nvars++); + new_localvarliteral(ls, "(for control)", nvars++); + /* create declared variables */ + new_localvar(ls, indexname, nvars++); + while (testnext(ls, ',')) + new_localvar(ls, str_checkname(ls), nvars++); + checknext(ls, TK_IN); + line = ls->linenumber; + adjust_assign(ls, 3, explist1(ls, &e), &e); + luaK_checkstack(fs, 3); /* extra space to call generator */ + forbody(ls, base, line, nvars - 3, 0); +} + + +static void ICACHE_FLASH_ATTR forstat (LexState *ls, int line) { + /* forstat -> FOR (fornum | forlist) END */ + FuncState *fs = ls->fs; + TString *varname; + BlockCnt bl; + enterblock(fs, &bl, 1); /* scope for loop and control variables */ + luaX_next(ls); /* skip `for' */ + varname = str_checkname(ls); /* first variable name */ + switch (ls->t.token) { + case '=': fornum(ls, varname, line); break; + case ',': case TK_IN: forlist(ls, varname); break; + default: luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected"); + } + check_match(ls, TK_END, TK_FOR, line); + leaveblock(fs); /* loop scope (`break' jumps to this point) */ +} + + +static int ICACHE_FLASH_ATTR test_then_block (LexState *ls) { + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + int condexit; + luaX_next(ls); /* skip IF or ELSEIF */ + condexit = cond(ls); + checknext(ls, TK_THEN); + block(ls); /* `then' part */ + return condexit; +} + + +static void ICACHE_FLASH_ATTR ifstat (LexState *ls, int line) { + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ + FuncState *fs = ls->fs; + int flist; + int escapelist = NO_JUMP; + flist = test_then_block(ls); /* IF cond THEN block */ + while (ls->t.token == TK_ELSEIF) { + luaK_concat(fs, &escapelist, luaK_jump(fs)); + luaK_patchtohere(fs, flist); + flist = test_then_block(ls); /* ELSEIF cond THEN block */ + } + if (ls->t.token == TK_ELSE) { + luaK_concat(fs, &escapelist, luaK_jump(fs)); + luaK_patchtohere(fs, flist); + luaX_next(ls); /* skip ELSE (after patch, for correct line info) */ + block(ls); /* `else' part */ + } + else + luaK_concat(fs, &escapelist, flist); + luaK_patchtohere(fs, escapelist); + check_match(ls, TK_END, TK_IF, line); +} + + +static void ICACHE_FLASH_ATTR localfunc (LexState *ls) { + expdesc v, b; + FuncState *fs = ls->fs; + new_localvar(ls, str_checkname(ls), 0); + init_exp(&v, VLOCAL, fs->freereg); + luaK_reserveregs(fs, 1); + adjustlocalvars(ls, 1); + body(ls, &b, 0, ls->linenumber); + luaK_storevar(fs, &v, &b); + /* debug information will only see the variable after this point! */ + getlocvar(fs, fs->nactvar - 1).startpc = fs->pc; +} + + +static void ICACHE_FLASH_ATTR localstat (LexState *ls) { + /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */ + int nvars = 0; + int nexps; + expdesc e; + do { + new_localvar(ls, str_checkname(ls), nvars++); + } while (testnext(ls, ',')); + if (testnext(ls, '=')) + nexps = explist1(ls, &e); + else { + e.k = VVOID; + nexps = 0; + } + adjust_assign(ls, nvars, nexps, &e); + adjustlocalvars(ls, nvars); +} + + +static int ICACHE_FLASH_ATTR funcname (LexState *ls, expdesc *v) { + /* funcname -> NAME {field} [`:' NAME] */ + int needself = 0; + singlevar(ls, v); + while (ls->t.token == '.') + field(ls, v); + if (ls->t.token == ':') { + needself = 1; + field(ls, v); + } + return needself; +} + + +static void ICACHE_FLASH_ATTR funcstat (LexState *ls, int line) { + /* funcstat -> FUNCTION funcname body */ + int needself; + expdesc v, b; + luaX_next(ls); /* skip FUNCTION */ + needself = funcname(ls, &v); + body(ls, &b, needself, line); + luaK_storevar(ls->fs, &v, &b); + luaK_fixline(ls->fs, line); /* definition `happens' in the first line */ +} + + +static void ICACHE_FLASH_ATTR exprstat (LexState *ls) { + /* stat -> func | assignment */ + FuncState *fs = ls->fs; + struct LHS_assign v; + primaryexp(ls, &v.v); + if (v.v.k == VCALL) /* stat -> func */ + SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */ + else { /* stat -> assignment */ + v.prev = NULL; + assignment(ls, &v, 1); + } +} + + +static void ICACHE_FLASH_ATTR retstat (LexState *ls) { + /* stat -> RETURN explist */ + FuncState *fs = ls->fs; + expdesc e; + int first, nret; /* registers with returned values */ + luaX_next(ls); /* skip RETURN */ + if (block_follow(ls->t.token) || ls->t.token == ';') + first = nret = 0; /* return no values */ + else { + nret = explist1(ls, &e); /* optional return values */ + if (hasmultret(e.k)) { + luaK_setmultret(fs, &e); + if (e.k == VCALL && nret == 1) { /* tail call? */ + SET_OPCODE(getcode(fs,&e), OP_TAILCALL); + lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar); + } + first = fs->nactvar; + nret = LUA_MULTRET; /* return all values */ + } + else { + if (nret == 1) /* only one single value? */ + first = luaK_exp2anyreg(fs, &e); + else { + luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */ + first = fs->nactvar; /* return all `active' values */ + lua_assert(nret == fs->freereg - first); + } + } + } + luaK_ret(fs, first, nret); +} + + +static int ICACHE_FLASH_ATTR statement (LexState *ls) { + int line = ls->linenumber; /* may be needed for error messages */ + switch (ls->t.token) { + case TK_IF: { /* stat -> ifstat */ + ifstat(ls, line); + return 0; + } + case TK_WHILE: { /* stat -> whilestat */ + whilestat(ls, line); + return 0; + } + case TK_DO: { /* stat -> DO block END */ + luaX_next(ls); /* skip DO */ + block(ls); + check_match(ls, TK_END, TK_DO, line); + return 0; + } + case TK_FOR: { /* stat -> forstat */ + forstat(ls, line); + return 0; + } + case TK_REPEAT: { /* stat -> repeatstat */ + repeatstat(ls, line); + return 0; + } + case TK_FUNCTION: { + funcstat(ls, line); /* stat -> funcstat */ + return 0; + } + case TK_LOCAL: { /* stat -> localstat */ + luaX_next(ls); /* skip LOCAL */ + if (testnext(ls, TK_FUNCTION)) /* local function? */ + localfunc(ls); + else + localstat(ls); + return 0; + } + case TK_RETURN: { /* stat -> retstat */ + retstat(ls); + return 1; /* must be last statement */ + } + case TK_BREAK: { /* stat -> breakstat */ + luaX_next(ls); /* skip BREAK */ + breakstat(ls); + return 1; /* must be last statement */ + } + default: { + exprstat(ls); + return 0; /* to avoid warnings */ + } + } +} + + +static void ICACHE_FLASH_ATTR chunk (LexState *ls) { + /* chunk -> { stat [`;'] } */ + int islast = 0; + enterlevel(ls); + while (!islast && !block_follow(ls->t.token)) { + islast = statement(ls); + testnext(ls, ';'); + lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && + ls->fs->freereg >= ls->fs->nactvar); + ls->fs->freereg = ls->fs->nactvar; /* free registers */ + } + leavelevel(ls); +} + +/* }====================================================================== */ diff --git a/app/lua/lparser.h b/app/lua/lparser.h new file mode 100644 index 00000000..18836afd --- /dev/null +++ b/app/lua/lparser.h @@ -0,0 +1,82 @@ +/* +** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + +#ifndef lparser_h +#define lparser_h + +#include "llimits.h" +#include "lobject.h" +#include "lzio.h" + + +/* +** Expression descriptor +*/ + +typedef enum { + VVOID, /* no value */ + VNIL, + VTRUE, + VFALSE, + VK, /* info = index of constant in `k' */ + VKNUM, /* nval = numerical value */ + VLOCAL, /* info = local register */ + VUPVAL, /* info = index of upvalue in `upvalues' */ + VGLOBAL, /* info = index of table; aux = index of global name in `k' */ + VINDEXED, /* info = table register; aux = index register (or `k') */ + VJMP, /* info = instruction pc */ + VRELOCABLE, /* info = instruction pc */ + VNONRELOC, /* info = result register */ + VCALL, /* info = instruction pc */ + VVARARG /* info = instruction pc */ +} expkind; + +typedef struct expdesc { + expkind k; + union { + struct { int info, aux; } s; + lua_Number nval; + } u; + int t; /* patch list of `exit when true' */ + int f; /* patch list of `exit when false' */ +} expdesc; + + +typedef struct upvaldesc { + lu_byte k; + lu_byte info; +} upvaldesc; + + +struct BlockCnt; /* defined in lparser.c */ + + +/* state needed to generate code for a given function */ +typedef struct FuncState { + Proto *f; /* current function header */ + Table *h; /* table to find (and reuse) elements in `k' */ + struct FuncState *prev; /* enclosing function */ + struct LexState *ls; /* lexical state */ + struct lua_State *L; /* copy of the Lua state */ + struct BlockCnt *bl; /* chain of current blocks */ + int pc; /* next position to code (equivalent to `ncode') */ + int lasttarget; /* `pc' of last `jump target' */ + int jpc; /* list of pending jumps to `pc' */ + int freereg; /* first free register */ + int nk; /* number of elements in `k' */ + int np; /* number of elements in `p' */ + short nlocvars; /* number of elements in `locvars' */ + lu_byte nactvar; /* number of active local variables */ + upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */ + unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */ +} FuncState; + + +LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + const char *name); + + +#endif diff --git a/app/lua/lrodefs.h b/app/lua/lrodefs.h new file mode 100644 index 00000000..02b635ef --- /dev/null +++ b/app/lua/lrodefs.h @@ -0,0 +1,41 @@ +/* Read-only tables helper */ + +#ifndef lrodefs_h +#define lrodefs_h + +#include "lrotable.h" + +#undef LUA_REG_TYPE +#undef LSTRKEY +#undef LNILKEY +#undef LNUMKEY +#undef LFUNCVAL +#undef LNUMVAL +#undef LROVAL +#undef LNILVAL +#undef LREGISTER + +#if (MIN_OPT_LEVEL > 0) && (LUA_OPTIMIZE_MEMORY >= MIN_OPT_LEVEL) +#define LUA_REG_TYPE luaR_entry +#define LSTRKEY LRO_STRKEY +#define LNUMKEY LRO_NUMKEY +#define LNILKEY LRO_NILKEY +#define LFUNCVAL LRO_FUNCVAL +#define LNUMVAL LRO_NUMVAL +#define LROVAL LRO_ROVAL +#define LNILVAL LRO_NILVAL +#define LREGISTER(L, name, table)\ + return 0 +#else +#define LUA_REG_TYPE luaL_reg +#define LSTRKEY(x) x +#define LNILKEY NULL +#define LFUNCVAL(x) x +#define LNILVAL NULL +#define LREGISTER(L, name, table)\ + luaL_register(L, name, table);\ + return 1 +#endif + +#endif /* lrodefs_h */ + diff --git a/app/lua/lrotable.c b/app/lua/lrotable.c new file mode 100644 index 00000000..60cf6788 --- /dev/null +++ b/app/lua/lrotable.c @@ -0,0 +1,135 @@ +/* Read-only tables for Lua */ + +#include "c_string.h" +#include "lrotable.h" +#include "lua.h" +#include "lauxlib.h" +#include "lstring.h" +#include "lobject.h" +#include "lapi.h" + +/* Local defines */ +#define LUAR_FINDFUNCTION 0 +#define LUAR_FINDVALUE 1 + +/* Externally defined read-only table array */ +extern const luaR_table lua_rotable[]; + +/* Find a global "read only table" in the constant lua_rotable array */ +void* ICACHE_FLASH_ATTR luaR_findglobal(const char *name, unsigned len) { + unsigned i; + + if (c_strlen(name) > LUA_MAX_ROTABLE_NAME) + return NULL; + for (i=0; lua_rotable[i].name; i ++) + if (*lua_rotable[i].name != '\0' && c_strlen(lua_rotable[i].name) == len && !c_strncmp(lua_rotable[i].name, name, len)) { + return (void*)(lua_rotable[i].pentries); + } + return NULL; +} + +/* Find an entry in a rotable and return it */ +static const TValue* ICACHE_FLASH_ATTR luaR_auxfind(const luaR_entry *pentry, const char *strkey, luaR_numkey numkey, unsigned *ppos) { + const TValue *res = NULL; + unsigned i = 0; + + if (pentry == NULL) + return NULL; + while(pentry->key.type != LUA_TNIL) { + if ((strkey && (pentry->key.type == LUA_TSTRING) && (!c_strcmp(pentry->key.id.strkey, strkey))) || + (!strkey && (pentry->key.type == LUA_TNUMBER) && ((luaR_numkey)pentry->key.id.numkey == numkey))) { + res = &pentry->value; + break; + } + i ++; pentry ++; + } + if (res && ppos) + *ppos = i; + return res; +} + +int ICACHE_FLASH_ATTR luaR_findfunction(lua_State *L, const luaR_entry *ptable) { + const TValue *res = NULL; + const char *key = luaL_checkstring(L, 2); + + res = luaR_auxfind(ptable, key, 0, NULL); + if (res && ttislightfunction(res)) { + luaA_pushobject(L, res); + return 1; + } + else + return 0; +} + +/* Find an entry in a rotable and return its type + If "strkey" is not NULL, the function will look for a string key, + otherwise it will look for a number key */ +const TValue* ICACHE_FLASH_ATTR luaR_findentry(void *data, const char *strkey, luaR_numkey numkey, unsigned *ppos) { + return luaR_auxfind((const luaR_entry*)data, strkey, numkey, ppos); +} + +/* Find the metatable of a given table */ +void* ICACHE_FLASH_ATTR luaR_getmeta(void *data) { +#ifdef LUA_META_ROTABLES + const TValue *res = luaR_auxfind((const luaR_entry*)data, "__metatable", 0, NULL); + return res && ttisrotable(res) ? rvalue(res) : NULL; +#else + return NULL; +#endif +} + +static void ICACHE_FLASH_ATTR luaR_next_helper(lua_State *L, const luaR_entry *pentries, int pos, TValue *key, TValue *val) { + setnilvalue(key); + setnilvalue(val); + if (pentries[pos].key.type != LUA_TNIL) { + /* Found an entry */ + if (pentries[pos].key.type == LUA_TSTRING) + setsvalue(L, key, luaS_newro(L, pentries[pos].key.id.strkey)) + else + setnvalue(key, (lua_Number)pentries[pos].key.id.numkey) + setobj2s(L, val, &pentries[pos].value); + } +} +/* next (used for iteration) */ +void ICACHE_FLASH_ATTR luaR_next(lua_State *L, void *data, TValue *key, TValue *val) { + const luaR_entry* pentries = (const luaR_entry*)data; + char strkey[LUA_MAX_ROTABLE_NAME + 1], *pstrkey = NULL; + luaR_numkey numkey = 0; + unsigned keypos; + + /* Special case: if key is nil, return the first element of the rotable */ + if (ttisnil(key)) + luaR_next_helper(L, pentries, 0, key, val); + else if (ttisstring(key) || ttisnumber(key)) { + /* Find the previoud key again */ + if (ttisstring(key)) { + luaR_getcstr(strkey, rawtsvalue(key), LUA_MAX_ROTABLE_NAME); + pstrkey = strkey; + } else + numkey = (luaR_numkey)nvalue(key); + luaR_findentry(data, pstrkey, numkey, &keypos); + /* Advance to next key */ + keypos ++; + luaR_next_helper(L, pentries, keypos, key, val); + } +} + +/* Convert a Lua string to a C string */ +void ICACHE_FLASH_ATTR luaR_getcstr(char *dest, const TString *src, size_t maxsize) { + if (src->tsv.len+1 > maxsize) + dest[0] = '\0'; + else { + c_memcpy(dest, getstr(src), src->tsv.len); + dest[src->tsv.len] = '\0'; + } +} + +/* Return 1 if the given pointer is a rotable */ +#ifdef LUA_META_ROTABLES + +#include "compiler.h" + +int ICACHE_FLASH_ATTR luaR_isrotable(void *p) { + return RODATA_START_ADDRESS <= (char*)p && (char*)p <= RODATA_END_ADDRESS; +} +#endif diff --git a/app/lua/lrotable.h b/app/lua/lrotable.h new file mode 100644 index 00000000..27de84a0 --- /dev/null +++ b/app/lua/lrotable.h @@ -0,0 +1,77 @@ +/* Read-only tables for Lua */ + +#ifndef lrotable_h +#define lrotable_h + +#include "lua.h" +#include "llimits.h" +#include "lobject.h" +#include "luaconf.h" + +/* Macros one can use to define rotable entries */ +#ifndef LUA_PACK_VALUE +#define LRO_FUNCVAL(v) {{.p = v}, LUA_TLIGHTFUNCTION} +#define LRO_NUMVAL(v) {{.n = v}, LUA_TNUMBER} +#define LRO_ROVAL(v) {{.p = (void*)v}, LUA_TROTABLE} +#define LRO_NILVAL {{.p = NULL}, LUA_TNIL} +#else // #ifndef LUA_PACK_VALUE +#define LRO_NUMVAL(v) {.value.n = v} +#ifdef ELUA_ENDIAN_LITTLE +#define LRO_FUNCVAL(v) {{(int)v, add_sig(LUA_TLIGHTFUNCTION)}} +#define LRO_ROVAL(v) {{(int)v, add_sig(LUA_TROTABLE)}} +#define LRO_NILVAL {{0, add_sig(LUA_TNIL)}} +#else // #ifdef ELUA_ENDIAN_LITTLE +#define LRO_FUNCVAL(v) {{add_sig(LUA_TLIGHTFUNCTION), (int)v}} +#define LRO_ROVAL(v) {{add_sig(LUA_TROTABLE), (int)v}} +#define LRO_NILVAL {{add_sig(LUA_TNIL), 0}} +#endif // #ifdef ELUA_ENDIAN_LITTLE +#endif // #ifndef LUA_PACK_VALUE + +#define LRO_STRKEY(k) {LUA_TSTRING, {.strkey = k}} +#define LRO_NUMKEY(k) {LUA_TNUMBER, {.numkey = k}} +#define LRO_NILKEY {LUA_TNIL, {.strkey=NULL}} + +/* Maximum length of a rotable name and of a string key*/ +#define LUA_MAX_ROTABLE_NAME 32 + +/* Type of a numeric key in a rotable */ +typedef int luaR_numkey; + +/* The next structure defines the type of a key */ +typedef struct +{ + int type; + union + { + const char* strkey; + luaR_numkey numkey; + } id; +} luaR_key; + +/* An entry in the read only table */ +typedef struct +{ + const luaR_key key; + const TValue value; +} luaR_entry; + +/* A rotable */ +typedef struct +{ + const char *name; + const luaR_entry *pentries; +} luaR_table; + +void* luaR_findglobal(const char *key, unsigned len); +int luaR_findfunction(lua_State *L, const luaR_entry *ptable); +const TValue* luaR_findentry(void *data, const char *strkey, luaR_numkey numkey, unsigned *ppos); +void luaR_getcstr(char *dest, const TString *src, size_t maxsize); +void luaR_next(lua_State *L, void *data, TValue *key, TValue *val); +void* luaR_getmeta(void *data); +#ifdef LUA_META_ROTABLES +int luaR_isrotable(void *p); +#else +#define luaR_isrotable(p) (0) +#endif + +#endif diff --git a/app/lua/lstate.c b/app/lua/lstate.c new file mode 100644 index 00000000..75454af1 --- /dev/null +++ b/app/lua/lstate.c @@ -0,0 +1,246 @@ +/* +** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $ +** Global State +** See Copyright Notice in lua.h +*/ + + +#include "c_stddef.h" + +#define lstate_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + +#define state_size(x) (sizeof(x) + LUAI_EXTRASPACE) +#define fromstate(l) (cast(lu_byte *, (l)) - LUAI_EXTRASPACE) +#define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE)) + + +/* +** Main thread combines a thread state and the global state +*/ +typedef struct LG { + lua_State l; + global_State g; +} LG; + + + +static void ICACHE_FLASH_ATTR stack_init (lua_State *L1, lua_State *L) { + /* initialize CallInfo array */ + L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo); + L1->ci = L1->base_ci; + L1->size_ci = BASIC_CI_SIZE; + L1->end_ci = L1->base_ci + L1->size_ci - 1; + /* initialize stack array */ + L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue); + L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK; + L1->top = L1->stack; + L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1; + /* initialize first ci */ + L1->ci->func = L1->top; + setnilvalue(L1->top++); /* `function' entry for this `ci' */ + L1->base = L1->ci->base = L1->top; + L1->ci->top = L1->top + LUA_MINSTACK; +} + + +static void ICACHE_FLASH_ATTR freestack (lua_State *L, lua_State *L1) { + luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo); + luaM_freearray(L, L1->stack, L1->stacksize, TValue); +} + + +/* +** open parts that may cause memory-allocation errors +*/ +static void ICACHE_FLASH_ATTR f_luaopen (lua_State *L, void *ud) { + global_State *g = G(L); + UNUSED(ud); + stack_init(L, L); /* init stack */ + sethvalue(L, gt(L), luaH_new(L, 0, 2)); /* table of globals */ + sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */ + luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ + luaT_init(L); + luaX_init(L); + luaS_fix(luaS_newliteral(L, MEMERRMSG)); + g->GCthreshold = 4*g->totalbytes; +} + + +static void ICACHE_FLASH_ATTR preinit_state (lua_State *L, global_State *g) { + G(L) = g; + L->stack = NULL; + L->stacksize = 0; + L->errorJmp = NULL; + L->hook = NULL; + L->hookmask = 0; + L->basehookcount = 0; + L->allowhook = 1; + resethookcount(L); + L->openupval = NULL; + L->size_ci = 0; + L->nCcalls = L->baseCcalls = 0; + L->status = 0; + L->base_ci = L->ci = NULL; + L->savedpc = NULL; + L->errfunc = 0; + setnilvalue(gt(L)); +} + + +static void ICACHE_FLASH_ATTR close_state (lua_State *L) { + global_State *g = G(L); + luaF_close(L, L->stack); /* close all upvalues for this thread */ + luaC_freeall(L); /* collect all objects */ + lua_assert(g->rootgc == obj2gco(L)); + lua_assert(g->strt.nuse == 0); + luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *); + luaZ_freebuffer(L, &g->buff); + freestack(L, L); + lua_assert(g->totalbytes == sizeof(LG)); + (*g->frealloc)(g->ud, fromstate(L), state_size(LG), 0); +} + + +lua_State *ICACHE_FLASH_ATTR luaE_newthread (lua_State *L) { + lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State))); + luaC_link(L, obj2gco(L1), LUA_TTHREAD); + setthvalue(L, L->top, L1); /* put thread on stack */ + incr_top(L); + preinit_state(L1, G(L)); + stack_init(L1, L); /* init stack */ + setobj2n(L, gt(L1), gt(L)); /* share table of globals */ + L1->hookmask = L->hookmask; + L1->basehookcount = L->basehookcount; + L1->hook = L->hook; + resethookcount(L1); + lua_assert(!isdead(G(L), obj2gco(L1))); + L->top--; /* remove thread from stack */ + return L1; +} + + +void ICACHE_FLASH_ATTR luaE_freethread (lua_State *L, lua_State *L1) { + luaF_close(L1, L1->stack); /* close all upvalues for this thread */ + lua_assert(L1->openupval == NULL); + luai_userstatefree(L1); + freestack(L, L1); + luaM_freemem(L, fromstate(L1), state_size(lua_State)); +} + + +LUA_API lua_State *ICACHE_FLASH_ATTR lua_newstate (lua_Alloc f, void *ud) { + int i; + lua_State *L; + global_State *g; + void *l = (*f)(ud, NULL, 0, state_size(LG)); + if (l == NULL) return NULL; + L = tostate(l); + g = &((LG *)L)->g; + L->next = NULL; + L->tt = LUA_TTHREAD; + g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT); + L->marked = luaC_white(g); + set2bits(L->marked, FIXEDBIT, SFIXEDBIT); + preinit_state(L, g); + g->frealloc = f; + g->ud = ud; + g->mainthread = L; + g->uvhead.u.l.prev = &g->uvhead; + g->uvhead.u.l.next = &g->uvhead; + g->GCthreshold = 0; /* mark it as unfinished state */ + g->estimate = 0; + g->strt.size = 0; + g->strt.nuse = 0; + g->strt.hash = NULL; + setnilvalue(registry(L)); + luaZ_initbuffer(L, &g->buff); + g->panic = NULL; + g->gcstate = GCSpause; + g->gcflags = GCFlagsNone; + g->rootgc = obj2gco(L); + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + g->tmudata = NULL; + g->totalbytes = sizeof(LG); + g->memlimit = 0; + g->gcpause = LUAI_GCPAUSE; + g->gcstepmul = LUAI_GCMUL; + g->gcdept = 0; +#ifdef EGC_INITIAL_MODE + g->egcmode = EGC_INITIAL_MODE; +#else + g->egcmode = 0; +#endif +#ifdef EGC_INITIAL_MEMLIMIT + g->memlimit = EGC_INITIAL_MEMLIMIT; +#else + g->memlimit = 0; +#endif + for (i=0; imt[i] = NULL; + if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) { + /* memory allocation error: free partial state */ + close_state(L); + L = NULL; + } + else + luai_userstateopen(L); + return L; +} + + +static void ICACHE_FLASH_ATTR callallgcTM (lua_State *L, void *ud) { + UNUSED(ud); + luaC_callGCTM(L); /* call GC metamethods for all udata */ +} + +// BogdanM: modified for eLua interrupt support +extern lua_State *luaL_newstate (void); +static lua_State *lua_crtstate; + +lua_State *ICACHE_FLASH_ATTR lua_open(void) { + lua_crtstate = luaL_newstate(); + return lua_crtstate; +} + +lua_State *ICACHE_FLASH_ATTR lua_getstate(void) { + return lua_crtstate; +} +LUA_API void ICACHE_FLASH_ATTR lua_close (lua_State *L) { +#ifndef LUA_CROSS_COMPILER + lua_sethook( L, NULL, 0, 0 ); + lua_crtstate = NULL; + lua_pushnil( L ); +// lua_rawseti( L, LUA_REGISTRYINDEX, LUA_INT_HANDLER_KEY ); +#endif + L = G(L)->mainthread; /* only the main thread can be closed */ + lua_lock(L); + luaF_close(L, L->stack); /* close all upvalues for this thread */ + luaC_separateudata(L, 1); /* separate udata that have GC metamethods */ + L->errfunc = 0; /* no error function during GC metamethods */ + do { /* repeat until no more errors */ + L->ci = L->base_ci; + L->base = L->top = L->ci->base; + L->nCcalls = L->baseCcalls = 0; + } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0); + lua_assert(G(L)->tmudata == NULL); + luai_userstateclose(L); + close_state(L); +} + diff --git a/app/lua/lstate.h b/app/lua/lstate.h new file mode 100644 index 00000000..1b3c363a --- /dev/null +++ b/app/lua/lstate.h @@ -0,0 +1,171 @@ +/* +** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $ +** Global State +** See Copyright Notice in lua.h +*/ + +#ifndef lstate_h +#define lstate_h + +#include "lua.h" + +#include "lobject.h" +#include "ltm.h" +#include "lzio.h" + + + +struct lua_longjmp; /* defined in ldo.c */ + + +/* table of globals */ +#define gt(L) (&L->l_gt) + +/* registry */ +#define registry(L) (&G(L)->l_registry) + + +/* extra stack space to handle TM calls and some other extras */ +#define EXTRA_STACK 5 + + +#define BASIC_CI_SIZE 8 + +#define BASIC_STACK_SIZE (2*LUA_MINSTACK) + + + +typedef struct stringtable { + GCObject **hash; + lu_int32 nuse; /* number of elements */ + int size; +} stringtable; + + +/* +** informations about a call +*/ +typedef struct CallInfo { + StkId base; /* base for this function */ + StkId func; /* function index in the stack */ + StkId top; /* top for this function */ + const Instruction *savedpc; + int nresults; /* expected number of results from this function */ + int tailcalls; /* number of tail calls lost under this entry */ +} CallInfo; + + + +#define curr_func(L) (ttisfunction(L->ci->func) ? clvalue(L->ci->func) : NULL) +#define ci_func(ci) (ttisfunction((ci)->func) ? clvalue((ci)->func) : NULL) +#define f_isLua(ci) (!ttislightfunction((ci)->func) && !ci_func(ci)->c.isC) +#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci)) + + +/* +** `global state', shared by all threads of this state +*/ +typedef struct global_State { + stringtable strt; /* hash table for strings */ + lua_Alloc frealloc; /* function to reallocate memory */ + void *ud; /* auxiliary data to `frealloc' */ + lu_byte currentwhite; + lu_byte gcstate; /* state of garbage collector */ + lu_byte gcflags; /* flags for the garbage collector */ + int sweepstrgc; /* position of sweep in `strt' */ + GCObject *rootgc; /* list of all collectable objects */ + GCObject **sweepgc; /* position of sweep in `rootgc' */ + GCObject *gray; /* list of gray objects */ + GCObject *grayagain; /* list of objects to be traversed atomically */ + GCObject *weak; /* list of weak tables (to be cleared) */ + GCObject *tmudata; /* last element of list of userdata to be GC */ + Mbuffer buff; /* temporary buffer for string concatentation */ + lu_mem GCthreshold; + lu_mem totalbytes; /* number of bytes currently allocated */ + lu_mem memlimit; /* maximum number of bytes that can be allocated, 0 = no limit. */ + lu_mem estimate; /* an estimate of number of bytes actually in use */ + lu_mem gcdept; /* how much GC is `behind schedule' */ + int gcpause; /* size of pause between successive GCs */ + int gcstepmul; /* GC `granularity' */ + int egcmode; /* emergency garbage collection operation mode */ + lua_CFunction panic; /* to be called in unprotected errors */ + TValue l_registry; + struct lua_State *mainthread; + UpVal uvhead; /* head of double-linked list of all open upvalues */ + struct Table *mt[NUM_TAGS]; /* metatables for basic types */ + TString *tmname[TM_N]; /* array with tag-method names */ +} global_State; + + +/* +** `per thread' state +*/ +struct lua_State { + CommonHeader; + lu_byte status; + StkId top; /* first free slot in the stack */ + StkId base; /* base of current function */ + global_State *l_G; + CallInfo *ci; /* call info for current function */ + const Instruction *savedpc; /* `savedpc' of current function */ + StkId stack_last; /* last free slot in the stack */ + StkId stack; /* stack base */ + CallInfo *end_ci; /* points after end of ci array*/ + CallInfo *base_ci; /* array of CallInfo's */ + int stacksize; + int size_ci; /* size of array `base_ci' */ + unsigned short nCcalls; /* number of nested C calls */ + unsigned short baseCcalls; /* nested C calls when resuming coroutine */ + lu_byte hookmask; + lu_byte allowhook; + int basehookcount; + int hookcount; + lua_Hook hook; + TValue l_gt; /* table of globals */ + TValue env; /* temporary place for environments */ + GCObject *openupval; /* list of open upvalues in this stack */ + GCObject *gclist; + struct lua_longjmp *errorJmp; /* current error recover point */ + ptrdiff_t errfunc; /* current error handling function (stack index) */ +}; + + +#define G(L) (L->l_G) + + +/* +** Union of all collectable objects +*/ +union GCObject { + GCheader gch; + union TString ts; + union Udata u; + union Closure cl; + struct Table h; + struct Proto p; + struct UpVal uv; + struct lua_State th; /* thread */ +}; + + +/* macros to convert a GCObject into a specific value */ +#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts)) +#define gco2ts(o) (&rawgco2ts(o)->tsv) +#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u)) +#define gco2u(o) (&rawgco2u(o)->uv) +#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl)) +#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h)) +#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p)) +#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv)) +#define ngcotouv(o) \ + check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv)) +#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th)) + +/* macro to convert any Lua object into a GCObject */ +#define obj2gco(v) (cast(GCObject *, (v))) + +LUAI_FUNC lua_State *luaE_newthread (lua_State *L); +LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); + +#endif + diff --git a/app/lua/lstring.c b/app/lua/lstring.c new file mode 100644 index 00000000..61dc74cd --- /dev/null +++ b/app/lua/lstring.c @@ -0,0 +1,147 @@ +/* +** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ +** String table (keeps all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + + +#include "c_string.h" + +#define lstring_c +#define LUA_CORE + +#include "lua.h" + +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" + +#define LUAS_READONLY_STRING 1 +#define LUAS_REGULAR_STRING 0 + +void ICACHE_FLASH_ATTR luaS_resize (lua_State *L, int newsize) { + stringtable *tb; + int i; + tb = &G(L)->strt; + if (luaC_sweepstrgc(L) || newsize == tb->size || is_resizing_strings_gc(L)) + return; /* cannot resize during GC traverse or doesn't need to be resized */ + set_resizing_strings_gc(L); + if (newsize > tb->size) { + luaM_reallocvector(L, tb->hash, tb->size, newsize, GCObject *); + for (i=tb->size; ihash[i] = NULL; + } + /* rehash */ + for (i=0; isize; i++) { + GCObject *p = tb->hash[i]; + tb->hash[i] = NULL; + while (p) { /* for each node in the list */ + GCObject *next = p->gch.next; /* save next */ + unsigned int h = gco2ts(p)->hash; + int h1 = lmod(h, newsize); /* new position */ + lua_assert(cast_int(h%newsize) == lmod(h, newsize)); + p->gch.next = tb->hash[h1]; /* chain it */ + tb->hash[h1] = p; + p = next; + } + } + if (newsize < tb->size) + luaM_reallocvector(L, tb->hash, tb->size, newsize, GCObject *); + tb->size = newsize; + unset_resizing_strings_gc(L); +} + +static TString *ICACHE_FLASH_ATTR newlstr (lua_State *L, const char *str, size_t l, + unsigned int h, int readonly) { + TString *ts; + stringtable *tb; + if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char)) + luaM_toobig(L); + tb = &G(L)->strt; + if ((tb->nuse + 1) > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2) + luaS_resize(L, tb->size*2); /* too crowded */ + ts = cast(TString *, luaM_malloc(L, readonly ? sizeof(char**)+sizeof(TString) : (l+1)*sizeof(char)+sizeof(TString))); + ts->tsv.len = l; + ts->tsv.hash = h; + ts->tsv.marked = luaC_white(G(L)); + ts->tsv.tt = LUA_TSTRING; + if (!readonly) { + c_memcpy(ts+1, str, l*sizeof(char)); + ((char *)(ts+1))[l] = '\0'; /* ending 0 */ + } else { + *(char **)(ts+1) = (char *)str; + luaS_readonly(ts); + } + h = lmod(h, tb->size); + ts->tsv.next = tb->hash[h]; /* chain new entry */ + tb->hash[h] = obj2gco(ts); + tb->nuse++; + return ts; +} + + +static TString *ICACHE_FLASH_ATTR luaS_newlstr_helper (lua_State *L, const char *str, size_t l, int readonly) { + GCObject *o; + unsigned int h = cast(unsigned int, l); /* seed */ + size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ + size_t l1; + for (l1=l; l1>=step; l1-=step) /* compute hash */ + h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); + for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; + o != NULL; + o = o->gch.next) { + TString *ts = rawgco2ts(o); + if (ts->tsv.len == l && (c_memcmp(str, getstr(ts), l) == 0)) { + /* string may be dead */ + if (isdead(G(L), o)) changewhite(o); + return ts; + } + } + return newlstr(L, str, l, h, readonly); /* not found */ +} + +static int ICACHE_FLASH_ATTR lua_is_ptr_in_ro_area(const char *p) { +#ifdef LUA_CROSS_COMPILER + return 0; +#else + +#include "compiler.h" + + return p >= RODATA_START_ADDRESS && p <= RODATA_END_ADDRESS; +#endif +} + +TString *ICACHE_FLASH_ATTR luaS_newlstr (lua_State *L, const char *str, size_t l) { + // If the pointer is in a read-only memory and the string is at least 4 chars in length, + // create it as a read-only string instead + if(lua_is_ptr_in_ro_area(str) && l+1 > sizeof(char**) && l == c_strlen(str)) + return luaS_newlstr_helper(L, str, l, LUAS_READONLY_STRING); + else + return luaS_newlstr_helper(L, str, l, LUAS_REGULAR_STRING); +} + + +LUAI_FUNC TString *ICACHE_FLASH_ATTR luaS_newrolstr (lua_State *L, const char *str, size_t l) { + if(l+1 > sizeof(char**) && l == c_strlen(str)) + return luaS_newlstr_helper(L, str, l, LUAS_READONLY_STRING); + else // no point in creating a RO string, as it would actually be larger + return luaS_newlstr_helper(L, str, l, LUAS_REGULAR_STRING); +} + + +Udata *ICACHE_FLASH_ATTR luaS_newudata (lua_State *L, size_t s, Table *e) { + Udata *u; + if (s > MAX_SIZET - sizeof(Udata)) + luaM_toobig(L); + u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata))); + u->uv.marked = luaC_white(G(L)); /* is not finalized */ + u->uv.tt = LUA_TUSERDATA; + u->uv.len = s; + u->uv.metatable = NULL; + u->uv.env = e; + /* chain it on udata list (after main thread) */ + u->uv.next = G(L)->mainthread->next; + G(L)->mainthread->next = obj2gco(u); + return u; +} + diff --git a/app/lua/lstring.h b/app/lua/lstring.h new file mode 100644 index 00000000..b4cc3dda --- /dev/null +++ b/app/lua/lstring.h @@ -0,0 +1,34 @@ +/* +** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $ +** String table (keep all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + +#ifndef lstring_h +#define lstring_h + + +#include "lgc.h" +#include "lobject.h" +#include "lstate.h" + + +#define sizestring(s) (sizeof(union TString)+(luaS_isreadonly(s) ? sizeof(char **) : ((s)->len+1)*sizeof(char))) + +#define sizeudata(u) (sizeof(union Udata)+(u)->len) + +#define luaS_new(L, s) (luaS_newlstr(L, s, c_strlen(s))) +#define luaS_newro(L, s) (luaS_newrolstr(L, s, c_strlen(s))) +#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ + (sizeof(s)/sizeof(char))-1)) + +#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT) +#define luaS_readonly(s) l_setbit((s)->tsv.marked, READONLYBIT) +#define luaS_isreadonly(s) testbit((s)->marked, READONLYBIT) + +LUAI_FUNC void luaS_resize (lua_State *L, int newsize); +LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e); +LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); +LUAI_FUNC TString *luaS_newrolstr (lua_State *L, const char *str, size_t l); + +#endif diff --git a/app/lua/lstrlib.c b/app/lua/lstrlib.c new file mode 100644 index 00000000..1a6aeb4d --- /dev/null +++ b/app/lua/lstrlib.c @@ -0,0 +1,893 @@ +/* +** $Id: lstrlib.c,v 1.132.1.5 2010/05/14 15:34:19 roberto Exp $ +** Standard library for string operations and pattern-matching +** See Copyright Notice in lua.h +*/ + + +#include "c_ctype.h" +#include "c_stddef.h" +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_string.h" + +#define lstrlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "lrotable.h" + +/* macro to `unsign' a character */ +#define uchar(c) ((unsigned char)(c)) + + + +static int ICACHE_FLASH_ATTR str_len (lua_State *L) { + size_t l; + luaL_checklstring(L, 1, &l); + lua_pushinteger(L, l); + return 1; +} + + +static ptrdiff_t ICACHE_FLASH_ATTR posrelat (ptrdiff_t pos, size_t len) { + /* relative string position: negative means back from end */ + if (pos < 0) pos += (ptrdiff_t)len + 1; + return (pos >= 0) ? pos : 0; +} + + +static int ICACHE_FLASH_ATTR str_sub (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l); + ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l); + if (start < 1) start = 1; + if (end > (ptrdiff_t)l) end = (ptrdiff_t)l; + if (start <= end) + lua_pushlstring(L, s+start-1, end-start+1); + else lua_pushliteral(L, ""); + return 1; +} + + +static int ICACHE_FLASH_ATTR str_reverse (lua_State *L) { + size_t l; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + luaL_buffinit(L, &b); + while (l--) luaL_addchar(&b, s[l]); + luaL_pushresult(&b); + return 1; +} + + +static int ICACHE_FLASH_ATTR str_lower (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + luaL_buffinit(L, &b); + for (i=0; i 0) + luaL_addlstring(&b, s, l); + luaL_pushresult(&b); + return 1; +} + + +static int ICACHE_FLASH_ATTR str_byte (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l); + ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l); + int n, i; + if (posi <= 0) posi = 1; + if ((size_t)pose > l) pose = l; + if (posi > pose) return 0; /* empty interval; return no values */ + n = (int)(pose - posi + 1); + if (posi + n <= pose) /* overflow? */ + luaL_error(L, "string slice too long"); + luaL_checkstack(L, n, "string slice too long"); + for (i=0; i= ms->level || ms->capture[l].len == CAP_UNFINISHED) + return luaL_error(ms->L, "invalid capture index"); + return l; +} + + +static int ICACHE_FLASH_ATTR capture_to_close (MatchState *ms) { + int level = ms->level; + for (level--; level>=0; level--) + if (ms->capture[level].len == CAP_UNFINISHED) return level; + return luaL_error(ms->L, "invalid pattern capture"); +} + + +static const char *ICACHE_FLASH_ATTR classend (MatchState *ms, const char *p) { + switch (*p++) { + case L_ESC: { + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")"); + return p+1; + } + case '[': { + if (*p == '^') p++; + do { /* look for a `]' */ + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")"); + if (*(p++) == L_ESC && *p != '\0') + p++; /* skip escapes (e.g. `%]') */ + } while (*p != ']'); + return p+1; + } + default: { + return p; + } + } +} + + +static int ICACHE_FLASH_ATTR match_class (int c, int cl) { + int res; + switch (tolower(cl)) { + case 'a' : res = isalpha(c); break; + case 'c' : res = iscntrl(c); break; + case 'd' : res = isdigit(c); break; + case 'l' : res = islower(c); break; + case 'p' : res = ispunct(c); break; + case 's' : res = isspace(c); break; + case 'u' : res = isupper(c); break; + case 'w' : res = isalnum(c); break; + case 'x' : res = isxdigit(c); break; + case 'z' : res = (c == 0); break; + default: return (cl == c); + } + return (islower(cl) ? res : !res); +} + + +static int ICACHE_FLASH_ATTR matchbracketclass (int c, const char *p, const char *ec) { + int sig = 1; + if (*(p+1) == '^') { + sig = 0; + p++; /* skip the `^' */ + } + while (++p < ec) { + if (*p == L_ESC) { + p++; + if (match_class(c, uchar(*p))) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < ec)) { + p+=2; + if (uchar(*(p-2)) <= c && c <= uchar(*p)) + return sig; + } + else if (uchar(*p) == c) return sig; + } + return !sig; +} + + +static int ICACHE_FLASH_ATTR singlematch (int c, const char *p, const char *ep) { + switch (*p) { + case '.': return 1; /* matches any char */ + case L_ESC: return match_class(c, uchar(*(p+1))); + case '[': return matchbracketclass(c, p, ep-1); + default: return (uchar(*p) == c); + } +} + + +static const char *match (MatchState *ms, const char *s, const char *p); + + +static const char *ICACHE_FLASH_ATTR matchbalance (MatchState *ms, const char *s, + const char *p) { + if (*p == 0 || *(p+1) == 0) + luaL_error(ms->L, "unbalanced pattern"); + if (*s != *p) return NULL; + else { + int b = *p; + int e = *(p+1); + int cont = 1; + while (++s < ms->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } + else if (*s == b) cont++; + } + } + return NULL; /* string ends out of balance */ +} + + +static const char *ICACHE_FLASH_ATTR max_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + ptrdiff_t i = 0; /* counts maximum expand for item */ + while ((s+i)src_end && singlematch(uchar(*(s+i)), p, ep)) + i++; + /* keeps trying to match with the maximum repetitions */ + while (i>=0) { + const char *res = match(ms, (s+i), ep+1); + if (res) return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + + +static const char *ICACHE_FLASH_ATTR min_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + for (;;) { + const char *res = match(ms, s, ep+1); + if (res != NULL) + return res; + else if (ssrc_end && singlematch(uchar(*s), p, ep)) + s++; /* try with one more repetition */ + else return NULL; + } +} + + +static const char *ICACHE_FLASH_ATTR start_capture (MatchState *ms, const char *s, + const char *p, int what) { + const char *res; + int level = ms->level; + if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level+1; + if ((res=match(ms, s, p)) == NULL) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + + +static const char *ICACHE_FLASH_ATTR end_capture (MatchState *ms, const char *s, + const char *p) { + int l = capture_to_close(ms); + const char *res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ((res = match(ms, s, p)) == NULL) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + + +static const char *ICACHE_FLASH_ATTR match_capture (MatchState *ms, const char *s, int l) { + size_t len; + l = check_capture(ms, l); + len = ms->capture[l].len; + if ((size_t)(ms->src_end-s) >= len && + c_memcmp(ms->capture[l].init, s, len) == 0) + return s+len; + else return NULL; +} + + +static const char *ICACHE_FLASH_ATTR match (MatchState *ms, const char *s, const char *p) { + init: /* using goto's to optimize tail recursion */ + switch (*p) { + case '(': { /* start capture */ + if (*(p+1) == ')') /* position capture? */ + return start_capture(ms, s, p+2, CAP_POSITION); + else + return start_capture(ms, s, p+1, CAP_UNFINISHED); + } + case ')': { /* end capture */ + return end_capture(ms, s, p+1); + } + case L_ESC: { + switch (*(p+1)) { + case 'b': { /* balanced string? */ + s = matchbalance(ms, s, p+2); + if (s == NULL) return NULL; + p+=4; goto init; /* else return match(ms, s, p+4); */ + } + case 'f': { /* frontier? */ + const char *ep; char previous; + p += 2; + if (*p != '[') + luaL_error(ms->L, "missing " LUA_QL("[") " after " + LUA_QL("%%f") " in pattern"); + ep = classend(ms, p); /* points to what is next */ + previous = (s == ms->src_init) ? '\0' : *(s-1); + if (matchbracketclass(uchar(previous), p, ep-1) || + !matchbracketclass(uchar(*s), p, ep-1)) return NULL; + p=ep; goto init; /* else return match(ms, s, ep); */ + } + default: { + if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ + s = match_capture(ms, s, uchar(*(p+1))); + if (s == NULL) return NULL; + p+=2; goto init; /* else return match(ms, s, p+2) */ + } + goto dflt; /* case default */ + } + } + } + case '\0': { /* end of pattern */ + return s; /* match succeeded */ + } + case '$': { + if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ + return (s == ms->src_end) ? s : NULL; /* check end of string */ + else goto dflt; + } + default: dflt: { /* it is a pattern item */ + const char *ep = classend(ms, p); /* points to what is next */ + int m = ssrc_end && singlematch(uchar(*s), p, ep); + switch (*ep) { + case '?': { /* optional */ + const char *res; + if (m && ((res=match(ms, s+1, ep+1)) != NULL)) + return res; + p=ep+1; goto init; /* else return match(ms, s, ep+1); */ + } + case '*': { /* 0 or more repetitions */ + return max_expand(ms, s, p, ep); + } + case '+': { /* 1 or more repetitions */ + return (m ? max_expand(ms, s+1, p, ep) : NULL); + } + case '-': { /* 0 or more repetitions (minimum) */ + return min_expand(ms, s, p, ep); + } + default: { + if (!m) return NULL; + s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ + } + } + } + } +} + + + +static const char *ICACHE_FLASH_ATTR lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative `l1' */ + else { + const char *init; /* to search for a `*s2' inside `s1' */ + l2--; /* 1st char will be checked by `memchr' */ + l1 = l1-l2; /* `s2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (c_memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct `l1' and `s1' to try again */ + l1 -= init-s1; + s1 = init; + } + } + return NULL; /* not found */ + } +} + + +static void ICACHE_FLASH_ATTR push_onecapture (MatchState *ms, int i, const char *s, + const char *e) { + if (i >= ms->level) { + if (i == 0) /* ms->level == 0, too */ + lua_pushlstring(ms->L, s, e - s); /* add whole match */ + else + luaL_error(ms->L, "invalid capture index"); + } + else { + ptrdiff_t l = ms->capture[i].len; + if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); + if (l == CAP_POSITION) + lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); + else + lua_pushlstring(ms->L, ms->capture[i].init, l); + } +} + + +static int ICACHE_FLASH_ATTR push_captures (MatchState *ms, const char *s, const char *e) { + int i; + int nlevels = (ms->level == 0 && s) ? 1 : ms->level; + luaL_checkstack(ms->L, nlevels, "too many captures"); + for (i = 0; i < nlevels; i++) + push_onecapture(ms, i, s, e); + return nlevels; /* number of strings pushed */ +} + + +static int ICACHE_FLASH_ATTR str_find_aux (lua_State *L, int find) { + size_t l1, l2; + const char *s = luaL_checklstring(L, 1, &l1); + const char *p = luaL_checklstring(L, 2, &l2); + ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1; + if (init < 0) init = 0; + else if ((size_t)(init) > l1) init = (ptrdiff_t)l1; + if (find && (lua_toboolean(L, 4) || /* explicit request? */ + c_strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */ + /* do a plain search */ + const char *s2 = lmemfind(s+init, l1-init, p, l2); + if (s2) { + lua_pushinteger(L, s2-s+1); + lua_pushinteger(L, s2-s+l2); + return 2; + } + } + else { + MatchState ms; + int anchor = (*p == '^') ? (p++, 1) : 0; + const char *s1=s+init; + ms.L = L; + ms.src_init = s; + ms.src_end = s+l1; + do { + const char *res; + ms.level = 0; + if ((res=match(&ms, s1, p)) != NULL) { + if (find) { + lua_pushinteger(L, s1-s+1); /* start */ + lua_pushinteger(L, res-s); /* end */ + return push_captures(&ms, NULL, 0) + 2; + } + else + return push_captures(&ms, s1, res); + } + } while (s1++ < ms.src_end && !anchor); + } + lua_pushnil(L); /* not found */ + return 1; +} + + +static int ICACHE_FLASH_ATTR str_find (lua_State *L) { + return str_find_aux(L, 1); +} + + +static int ICACHE_FLASH_ATTR str_match (lua_State *L) { + return str_find_aux(L, 0); +} + + +static int ICACHE_FLASH_ATTR gmatch_aux (lua_State *L) { + MatchState ms; + size_t ls; + const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); + const char *p = lua_tostring(L, lua_upvalueindex(2)); + const char *src; + ms.L = L; + ms.src_init = s; + ms.src_end = s+ls; + for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); + src <= ms.src_end; + src++) { + const char *e; + ms.level = 0; + if ((e = match(&ms, src, p)) != NULL) { + lua_Integer newstart = e-s; + if (e == src) newstart++; /* empty match? go at least one position */ + lua_pushinteger(L, newstart); + lua_replace(L, lua_upvalueindex(3)); + return push_captures(&ms, src, e); + } + } + return 0; /* not found */ +} + + +static int ICACHE_FLASH_ATTR gmatch (lua_State *L) { + luaL_checkstring(L, 1); + luaL_checkstring(L, 2); + lua_settop(L, 2); + lua_pushinteger(L, 0); + lua_pushcclosure(L, gmatch_aux, 3); + return 1; +} + +#if LUA_OPTIMIZE_MEMORY == 0 || !defined(LUA_COMPAT_GFIND) +static int ICACHE_FLASH_ATTR gfind_nodef (lua_State *L) { + return luaL_error(L, LUA_QL("string.gfind") " was renamed to " + LUA_QL("string.gmatch")); +} +#endif + +static void ICACHE_FLASH_ATTR add_s (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e) { + size_t l, i; + const char *news = lua_tolstring(ms->L, 3, &l); + for (i = 0; i < l; i++) { + if (news[i] != L_ESC) + luaL_addchar(b, news[i]); + else { + i++; /* skip ESC */ + if (!isdigit(uchar(news[i]))) + luaL_addchar(b, news[i]); + else if (news[i] == '0') + luaL_addlstring(b, s, e - s); + else { + push_onecapture(ms, news[i] - '1', s, e); + luaL_addvalue(b); /* add capture to accumulated result */ + } + } + } +} + + +static void ICACHE_FLASH_ATTR add_value (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e) { + lua_State *L = ms->L; + switch (lua_type(L, 3)) { + case LUA_TNUMBER: + case LUA_TSTRING: { + add_s(ms, b, s, e); + return; + } + case LUA_TFUNCTION: + case LUA_TLIGHTFUNCTION: { + int n; + lua_pushvalue(L, 3); + n = push_captures(ms, s, e); + lua_call(L, n, 1); + break; + } + case LUA_TTABLE: { + push_onecapture(ms, 0, s, e); + lua_gettable(L, 3); + break; + } + } + if (!lua_toboolean(L, -1)) { /* nil or false? */ + lua_pop(L, 1); + lua_pushlstring(L, s, e - s); /* keep original text */ + } + else if (!lua_isstring(L, -1)) + luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); + luaL_addvalue(b); /* add result to accumulator */ +} + + +static int ICACHE_FLASH_ATTR str_gsub (lua_State *L) { + size_t srcl; + const char *src = luaL_checklstring(L, 1, &srcl); + const char *p = luaL_checkstring(L, 2); + int tr = lua_type(L, 3); + int max_s = luaL_optint(L, 4, srcl+1); + int anchor = (*p == '^') ? (p++, 1) : 0; + int n = 0; + MatchState ms; + luaL_Buffer b; + luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || + tr == LUA_TFUNCTION || tr == LUA_TTABLE || + tr == LUA_TLIGHTFUNCTION, 3, + "string/function/table/lightfunction expected"); + luaL_buffinit(L, &b); + ms.L = L; + ms.src_init = src; + ms.src_end = src+srcl; + while (n < max_s) { + const char *e; + ms.level = 0; + e = match(&ms, src, p); + if (e) { + n++; + add_value(&ms, &b, src, e); + } + if (e && e>src) /* non empty match? */ + src = e; /* skip it */ + else if (src < ms.src_end) + luaL_addchar(&b, *src++); + else break; + if (anchor) break; + } + luaL_addlstring(&b, src, ms.src_end-src); + luaL_pushresult(&b); + lua_pushinteger(L, n); /* number of substitutions */ + return 2; +} + +/* }====================================================== */ + + +/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ +/* was 512, modified to 128 for eLua */ +#define MAX_ITEM 128 +/* valid flags in a format specification */ +#define FLAGS "-+ #0" +/* +** maximum size of each format specification (such as '%-099.99d') +** (+10 accounts for %99.99x plus margin of error) +*/ +#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10) + + +static void ICACHE_FLASH_ATTR addquoted (lua_State *L, luaL_Buffer *b, int arg) { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + luaL_addchar(b, '"'); + while (l--) { + switch (*s) { + case '"': case '\\': case '\n': { + luaL_addchar(b, '\\'); + luaL_addchar(b, *s); + break; + } + case '\r': { + luaL_addlstring(b, "\\r", 2); + break; + } + case '\0': { + luaL_addlstring(b, "\\000", 4); + break; + } + default: { + luaL_addchar(b, *s); + break; + } + } + s++; + } + luaL_addchar(b, '"'); +} + +static const char *ICACHE_FLASH_ATTR scanformat (lua_State *L, const char *strfrmt, char *form) { + const char *p = strfrmt; + while (*p != '\0' && c_strchr(FLAGS, *p) != NULL) p++; /* skip flags */ + if ((size_t)(p - strfrmt) >= sizeof(FLAGS)) + luaL_error(L, "invalid format (repeated flags)"); + if (isdigit(uchar(*p))) p++; /* skip width */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + if (*p == '.') { + p++; + if (isdigit(uchar(*p))) p++; /* skip precision */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + } + if (isdigit(uchar(*p))) + luaL_error(L, "invalid format (width or precision too long)"); + *(form++) = '%'; + c_strncpy(form, strfrmt, p - strfrmt + 1); + form += p - strfrmt + 1; + *form = '\0'; + return p; +} + + +static void ICACHE_FLASH_ATTR addintlen (char *form) { + size_t l = c_strlen(form); + char spec = form[l - 1]; + c_strcpy(form + l - 1, LUA_INTFRMLEN); + form[l + sizeof(LUA_INTFRMLEN) - 2] = spec; + form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0'; +} + + +static int ICACHE_FLASH_ATTR str_format (lua_State *L) { + int top = lua_gettop(L); + int arg = 1; + size_t sfl; + const char *strfrmt = luaL_checklstring(L, arg, &sfl); + const char *strfrmt_end = strfrmt+sfl; + luaL_Buffer b; + luaL_buffinit(L, &b); + while (strfrmt < strfrmt_end) { + if (*strfrmt != L_ESC) + luaL_addchar(&b, *strfrmt++); + else if (*++strfrmt == L_ESC) + luaL_addchar(&b, *strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT]; /* to store the format (`%...') */ + char buff[MAX_ITEM]; /* to store the formatted item */ + if (++arg > top) + luaL_argerror(L, arg, "no value"); + strfrmt = scanformat(L, strfrmt, form); + switch (*strfrmt++) { + case 'c': { + c_sprintf(buff, form, (int)luaL_checknumber(L, arg)); + break; + } + case 'd': case 'i': { + addintlen(form); + c_sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg)); + break; + } + case 'o': case 'u': case 'x': case 'X': { + addintlen(form); + c_sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg)); + break; + } +#if !defined LUA_NUMBER_INTEGRAL + case 'e': case 'E': case 'f': + case 'g': case 'G': { + c_sprintf(buff, form, (double)luaL_checknumber(L, arg)); + break; + } +#endif + case 'q': { + addquoted(L, &b, arg); + continue; /* skip the 'addsize' at the end */ + } + case 's': { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + if (!c_strchr(form, '.') && l >= 100) { + /* no precision and string is too long to be formatted; + keep original string */ + lua_pushvalue(L, arg); + luaL_addvalue(&b); + continue; /* skip the `addsize' at the end */ + } + else { + c_sprintf(buff, form, s); + break; + } + } + default: { /* also treat cases `pnLlh' */ + return luaL_error(L, "invalid option " LUA_QL("%%%c") " to " + LUA_QL("format"), *(strfrmt - 1)); + } + } + luaL_addlstring(&b, buff, c_strlen(buff)); + } + } + luaL_pushresult(&b); + return 1; +} + +#define MIN_OPT_LEVEL 1 +#include "lrodefs.h" +const LUA_REG_TYPE strlib[] = { + {LSTRKEY("byte"), LFUNCVAL(str_byte)}, + {LSTRKEY("char"), LFUNCVAL(str_char)}, + {LSTRKEY("dump"), LFUNCVAL(str_dump)}, + {LSTRKEY("find"), LFUNCVAL(str_find)}, + {LSTRKEY("format"), LFUNCVAL(str_format)}, +#if LUA_OPTIMIZE_MEMORY > 0 && defined(LUA_COMPAT_GFIND) + {LSTRKEY("gfind"), LFUNCVAL(gmatch)}, +#else + {LSTRKEY("gfind"), LFUNCVAL(gfind_nodef)}, +#endif + {LSTRKEY("gmatch"), LFUNCVAL(gmatch)}, + {LSTRKEY("gsub"), LFUNCVAL(str_gsub)}, + {LSTRKEY("len"), LFUNCVAL(str_len)}, + {LSTRKEY("lower"), LFUNCVAL(str_lower)}, + {LSTRKEY("match"), LFUNCVAL(str_match)}, + {LSTRKEY("rep"), LFUNCVAL(str_rep)}, + {LSTRKEY("reverse"), LFUNCVAL(str_reverse)}, + {LSTRKEY("sub"), LFUNCVAL(str_sub)}, + {LSTRKEY("upper"), LFUNCVAL(str_upper)}, +#if LUA_OPTIMIZE_MEMORY > 0 + {LSTRKEY("__index"), LROVAL(strlib)}, +#endif + {LNILKEY, LNILVAL} +}; + + +#if LUA_OPTIMIZE_MEMORY != 2 +static void ICACHE_FLASH_ATTR createmetatable (lua_State *L) { + lua_createtable(L, 0, 1); /* create metatable for strings */ + lua_pushliteral(L, ""); /* dummy string */ + lua_pushvalue(L, -2); + lua_setmetatable(L, -2); /* set string metatable */ + lua_pop(L, 1); /* pop dummy string */ + lua_pushvalue(L, -2); /* string library... */ + lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */ + lua_pop(L, 1); /* pop metatable */ +} +#endif + +/* +** Open string library +*/ +LUALIB_API int ICACHE_FLASH_ATTR luaopen_string (lua_State *L) { +#if LUA_OPTIMIZE_MEMORY == 0 + luaL_register(L, LUA_STRLIBNAME, strlib); +#if defined(LUA_COMPAT_GFIND) + lua_getfield(L, -1, "gmatch"); + lua_setfield(L, -2, "gfind"); +#endif + createmetatable(L); + return 1; +#else + lua_pushliteral(L,""); + lua_pushrotable(L, (void*)strlib); + lua_setmetatable(L, -2); + lua_pop(L,1); + return 0; +#endif +} + diff --git a/app/lua/ltable.c b/app/lua/ltable.c new file mode 100644 index 00000000..0c1162cd --- /dev/null +++ b/app/lua/ltable.c @@ -0,0 +1,759 @@ +/* +** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + + +/* +** Implementation of tables (aka arrays, objects, or hash tables). +** Tables keep its elements in two parts: an array part and a hash part. +** Non-negative integer keys are all candidates to be kept in the array +** part. The actual size of the array is the largest `n' such that at +** least half the slots between 0 and n are in use. +** Hash uses a mix of chained scatter table with Brent's variation. +** A main invariant of these tables is that, if an element is not +** in its main position (i.e. the `original' position that its hash gives +** to it), then the colliding element is in its own main position. +** Hence even when the load factor reaches 100%, performance remains good. +*/ + +#include "c_math.h" +#include "c_string.h" + +#define ltable_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "ltable.h" +#include "lrotable.h" + +/* +** max size of array part is 2^MAXBITS +*/ +#if LUAI_BITSINT > 26 +#define MAXBITS 26 +#else +#define MAXBITS (LUAI_BITSINT-2) +#endif + +#define MAXASIZE (1 << MAXBITS) + + +#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) + +#define hashstr(t,str) hashpow2(t, (str)->tsv.hash) +#define hashboolean(t,p) hashpow2(t, p) + + +/* +** for some types, it is better to avoid modulus by power of 2, as +** they tend to have many 2 factors. +*/ +#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) + + +#define hashpointer(t,p) hashmod(t, IntPoint(p)) + + +/* +** number of ints inside a lua_Number +*/ +#define numints cast_int(sizeof(lua_Number)/sizeof(int)) + + + +#define dummynode (&dummynode_) + +static const Node dummynode_ = { + {LUA_TVALUE_NIL}, /* value */ + {LUA_TKEY_NIL} /* key */ +}; + + +/* +** hash for lua_Numbers +*/ +static Node *ICACHE_FLASH_ATTR hashnum (const Table *t, lua_Number n) { + unsigned int a[numints]; + int i; + if (luai_numeq(n, 0)) /* avoid problems with -0 */ + return gnode(t, 0); + c_memcpy(a, &n, sizeof(a)); + for (i = 1; i < numints; i++) a[0] += a[i]; + return hashmod(t, a[0]); +} + + + +/* +** returns the `main' position of an element in a table (that is, the index +** of its hash value) +*/ +static Node *ICACHE_FLASH_ATTR mainposition (const Table *t, const TValue *key) { + switch (ttype(key)) { + case LUA_TNUMBER: + return hashnum(t, nvalue(key)); + case LUA_TSTRING: + return hashstr(t, rawtsvalue(key)); + case LUA_TBOOLEAN: + return hashboolean(t, bvalue(key)); + case LUA_TLIGHTUSERDATA: + case LUA_TROTABLE: + case LUA_TLIGHTFUNCTION: + return hashpointer(t, pvalue(key)); + default: + return hashpointer(t, gcvalue(key)); + } +} + + +/* +** returns the index for `key' if `key' is an appropriate key to live in +** the array part of the table, -1 otherwise. +*/ +static int ICACHE_FLASH_ATTR arrayindex (const TValue *key) { + if (ttisnumber(key)) { + lua_Number n = nvalue(key); + int k; + lua_number2int(k, n); + if (luai_numeq(cast_num(k), n)) + return k; + } + return -1; /* `key' did not match some condition */ +} + + +/* +** returns the index of a `key' for table traversals. First goes all +** elements in the array part, then elements in the hash part. The +** beginning of a traversal is signalled by -1. +*/ +static int ICACHE_FLASH_ATTR findindex (lua_State *L, Table *t, StkId key) { + int i; + if (ttisnil(key)) return -1; /* first iteration */ + i = arrayindex(key); + if (0 < i && i <= t->sizearray) /* is `key' inside array part? */ + return i-1; /* yes; that's the index (corrected to C) */ + else { + Node *n = mainposition(t, key); + do { /* check whether `key' is somewhere in the chain */ + /* key may be dead already, but it is ok to use it in `next' */ + if (luaO_rawequalObj(key2tval(n), key) || + (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) && + gcvalue(gkey(n)) == gcvalue(key))) { + i = cast_int(n - gnode(t, 0)); /* key index in hash table */ + /* hash elements are numbered after array ones */ + return i + t->sizearray; + } + else n = gnext(n); + } while (n); + luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */ + return 0; /* to avoid warnings */ + } +} + + +int ICACHE_FLASH_ATTR luaH_next (lua_State *L, Table *t, StkId key) { + int i = findindex(L, t, key); /* find original element */ + for (i++; i < t->sizearray; i++) { /* try first array part */ + if (!ttisnil(&t->array[i])) { /* a non-nil value? */ + setnvalue(key, cast_num(i+1)); + setobj2s(L, key+1, &t->array[i]); + return 1; + } + } + for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ + if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ + setobj2s(L, key, key2tval(gnode(t, i))); + setobj2s(L, key+1, gval(gnode(t, i))); + return 1; + } + } + return 0; /* no more elements */ +} + + +int ICACHE_FLASH_ATTR luaH_next_ro (lua_State *L, void *t, StkId key) { + luaR_next(L, t, key, key+1); + return ttisnil(key) ? 0 : 1; +} + + +/* +** {============================================================= +** Rehash +** ============================================================== +*/ + + +static int ICACHE_FLASH_ATTR computesizes (int nums[], int *narray) { + int i; + int twotoi; /* 2^i */ + int a = 0; /* number of elements smaller than 2^i */ + int na = 0; /* number of elements to go to array part */ + int n = 0; /* optimal size for array part */ + for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { + if (nums[i] > 0) { + a += nums[i]; + if (a > twotoi/2) { /* more than half elements present? */ + n = twotoi; /* optimal size (till now) */ + na = a; /* all elements smaller than n will go to array part */ + } + } + if (a == *narray) break; /* all elements already counted */ + } + *narray = n; + lua_assert(*narray/2 <= na && na <= *narray); + return na; +} + + +static int ICACHE_FLASH_ATTR countint (const TValue *key, int *nums) { + int k = arrayindex(key); + if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */ + nums[ceillog2(k)]++; /* count as such */ + return 1; + } + else + return 0; +} + + +static int ICACHE_FLASH_ATTR numusearray (const Table *t, int *nums) { + int lg; + int ttlg; /* 2^lg */ + int ause = 0; /* summation of `nums' */ + int i = 1; /* count to traverse all array keys */ + for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */ + int lc = 0; /* counter */ + int lim = ttlg; + if (lim > t->sizearray) { + lim = t->sizearray; /* adjust upper limit */ + if (i > lim) + break; /* no more elements to count */ + } + /* count elements in range (2^(lg-1), 2^lg] */ + for (; i <= lim; i++) { + if (!ttisnil(&t->array[i-1])) + lc++; + } + nums[lg] += lc; + ause += lc; + } + return ause; +} + + +static int ICACHE_FLASH_ATTR numusehash (const Table *t, int *nums, int *pnasize) { + int totaluse = 0; /* total number of elements */ + int ause = 0; /* summation of `nums' */ + int i = sizenode(t); + while (i--) { + Node *n = &t->node[i]; + if (!ttisnil(gval(n))) { + ause += countint(key2tval(n), nums); + totaluse++; + } + } + *pnasize += ause; + return totaluse; +} + + +static void ICACHE_FLASH_ATTR setarrayvector (lua_State *L, Table *t, int size) { + int i; + luaM_reallocvector(L, t->array, t->sizearray, size, TValue); + for (i=t->sizearray; iarray[i]); + t->sizearray = size; +} + + +static Node *ICACHE_FLASH_ATTR getfreepos (Table *t) { + while (t->lastfree-- > t->node) { + if (ttisnil(gkey(t->lastfree))) + return t->lastfree; + } + return NULL; /* could not find a free place */ +} + + +static void ICACHE_FLASH_ATTR resizenodevector (lua_State *L, Table *t, int oldsize, int newsize) { + int lsize; + if (newsize == 0) { /* no elements to hash part? */ + t->node = cast(Node *, dummynode); /* use common `dummynode' */ + lsize = 0; + } + else { + Node *node = t->node; + int i; + lsize = ceillog2(newsize); + if (lsize > MAXBITS) + luaG_runerror(L, "table overflow"); + newsize = twoto(lsize); + if (node == dummynode) { + oldsize = 0; + node = NULL; /* don't try to realloc `dummynode' pointer. */ + } + luaM_reallocvector(L, node, oldsize, newsize, Node); + t->node = node; + for (i=oldsize; ilsizenode = cast_byte(lsize); + t->lastfree = gnode(t, newsize); /* reset lastfree to end of table. */ +} + + +static Node *ICACHE_FLASH_ATTR find_prev_node(Node *mp, Node *next) { + Node *prev = mp; + while (prev != NULL && gnext(prev) != next) prev = gnext(prev); + return prev; +} + + +/* +** move a node from it's old position to it's new position during a rehash; +** first, check whether the moving node's main position is free. If not, check whether +** colliding node is in its main position or not: if it is not, move colliding +** node to an empty place and put moving node in its main position; otherwise +** (colliding node is in its main position), moving node goes to an empty position. +*/ +static int ICACHE_FLASH_ATTR move_node (lua_State *L, Table *t, Node *node) { + Node *mp = mainposition(t, key2tval(node)); + /* if node is in it's main position, don't need to move node. */ + if (mp == node) return 1; + /* if node is in it's main position's chain, don't need to move node. */ + if (find_prev_node(mp, node) != NULL) return 1; + /* is main position is free? */ + if (!ttisnil(gval(mp)) || mp == dummynode) { + /* no; move main position node if it is out of its main position */ + Node *othermp; + othermp = mainposition(t, key2tval(mp)); + if (othermp != mp) { /* is colliding node out of its main position? */ + /* yes; swap colliding node with the node that is being moved. */ + Node *prev; + Node tmp; + tmp = *node; + prev = find_prev_node(othermp, mp); /* find previous */ + if (prev != NULL) gnext(prev) = node; /* redo the chain with `n' in place of `mp' */ + *node = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + *mp = tmp; + return (prev != NULL) ? 1 : 0; /* is colliding node part of its main position chain? */ + } + else { /* colliding node is in its own main position */ + /* add node to main position's chain. */ + gnext(node) = gnext(mp); /* chain new position */ + gnext(mp) = node; + } + } + else { /* main position is free, move node */ + *mp = *node; + gnext(node) = NULL; + setnilvalue(gkey(node)); + setnilvalue(gval(node)); + } + return 1; +} + + +static int ICACHE_FLASH_ATTR move_number (lua_State *L, Table *t, Node *node) { + int key; + lua_Number n = nvalue(key2tval(node)); + lua_number2int(key, n); + if (luai_numeq(cast_num(key), nvalue(key2tval(node)))) {/* index is int? */ + /* (1 <= key && key <= t->sizearray) */ + if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) { + setobjt2t(L, &t->array[key-1], gval(node)); + setnilvalue(gkey(node)); + setnilvalue(gval(node)); + return 1; + } + } + return 0; +} + + +static void ICACHE_FLASH_ATTR resize_hashpart (lua_State *L, Table *t, int nhsize) { + int i; + int lsize=0; + int oldhsize = (t->node != dummynode) ? twoto(t->lsizenode) : 0; + if (nhsize > 0) { /* round new hashpart size up to next power of two. */ + lsize=ceillog2(nhsize); + if (lsize > MAXBITS) + luaG_runerror(L, "table overflow"); + } + nhsize = twoto(lsize); + /* grow hash part to new size. */ + if (oldhsize < nhsize) + resizenodevector(L, t, oldhsize, nhsize); + else { /* hash part might be shrinking */ + if (nhsize > 0) { + t->lsizenode = cast_byte(lsize); + t->lastfree = gnode(t, nhsize); /* reset lastfree back to end of table. */ + } + else { /* new hashpart size is zero. */ + resizenodevector(L, t, oldhsize, nhsize); + return; + } + } + /* break old chains, try moving int keys to array part and compact keys into new hashpart */ + for (i = 0; i < oldhsize; i++) { + Node *old = gnode(t, i); + gnext(old) = NULL; + if (ttisnil(gval(old))) { /* clear nodes with nil values. */ + setnilvalue(gkey(old)); + continue; + } + if (ttisnumber(key2tval(old))) { /* try moving the int keys into array part. */ + if(move_number(L, t, old)) + continue; + } + if (i >= nhsize) { /* move all valid keys to indices < nhsize. */ + Node *n = getfreepos(t); /* get a free place */ + lua_assert(n != dummynode && n != NULL); + *n = *old; + } + } + /* shrink hash part */ + if (oldhsize > nhsize) + resizenodevector(L, t, oldhsize, nhsize); + /* move nodes to their new mainposition and re-create node chains */ + for (i = 0; i < nhsize; i++) { + Node *curr = gnode(t, i); + if (!ttisnil(gval(curr))) + while (move_node(L, t, curr) == 0); + } +} + + +static void ICACHE_FLASH_ATTR resize (lua_State *L, Table *t, int nasize, int nhsize) { + int i; + int oldasize = t->sizearray; + if (nasize > oldasize) /* array part must grow? */ + setarrayvector(L, t, nasize); + resize_hashpart(L, t, nhsize); + if (nasize < oldasize) { /* array part must shrink? */ + t->sizearray = nasize; + /* re-insert elements from vanishing slice */ + for (i=nasize; iarray[i])) + setobjt2t(L, luaH_setnum(L, t, i+1), &t->array[i]); + } + /* shrink array */ + luaM_reallocvector(L, t->array, oldasize, nasize, TValue); + } +} + + +void ICACHE_FLASH_ATTR luaH_resizearray (lua_State *L, Table *t, int nasize) { + int nsize = (t->node == dummynode) ? 0 : sizenode(t); + resize(L, t, nasize, nsize); +} + + +static void ICACHE_FLASH_ATTR rehash (lua_State *L, Table *t, const TValue *ek) { + int nasize, na; + int nums[MAXBITS+1]; /* nums[i] = number of keys between 2^(i-1) and 2^i */ + int i; + int totaluse; + for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */ + nasize = numusearray(t, nums); /* count keys in array part */ + totaluse = nasize; /* all those keys are integer keys */ + totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ + /* count extra key */ + nasize += countint(ek, nums); + totaluse++; + /* compute new size for array part */ + na = computesizes(nums, &nasize); + /* resize the table to new computed sizes */ + resize(L, t, nasize, totaluse - na); +} + + + +/* +** }============================================================= +*/ + + +Table *ICACHE_FLASH_ATTR luaH_new (lua_State *L, int narray, int nhash) { + Table *t = luaM_new(L, Table); + luaC_link(L, obj2gco(t), LUA_TTABLE); + sethvalue2s(L, L->top, t); /* put table on stack */ + incr_top(L); + t->metatable = NULL; + t->flags = cast_byte(~0); + /* temporary values (kept only if some malloc fails) */ + t->array = NULL; + t->sizearray = 0; + t->lsizenode = 0; + t->node = cast(Node *, dummynode); + setarrayvector(L, t, narray); + resizenodevector(L, t, 0, nhash); + L->top--; /* remove table from stack */ + return t; +} + + +void ICACHE_FLASH_ATTR luaH_free (lua_State *L, Table *t) { + if (t->node != dummynode) + luaM_freearray(L, t->node, sizenode(t), Node); + luaM_freearray(L, t->array, t->sizearray, TValue); + luaM_free(L, t); +} + + + +/* +** inserts a new key into a hash table; first, check whether key's main +** position is free. If not, check whether colliding node is in its main +** position or not: if it is not, move colliding node to an empty place and +** put new key in its main position; otherwise (colliding node is in its main +** position), new key goes to an empty position. +*/ +static TValue *ICACHE_FLASH_ATTR newkey (lua_State *L, Table *t, const TValue *key) { + Node *mp = mainposition(t, key); + if (!ttisnil(gval(mp)) || mp == dummynode) { + Node *othern; + Node *n = getfreepos(t); /* get a free place */ + if (n == NULL) { /* cannot find a free place? */ + rehash(L, t, key); /* grow table */ + return luaH_set(L, t, key); /* re-insert key into grown table */ + } + lua_assert(n != dummynode); + othern = mainposition(t, key2tval(mp)); + if (othern != mp) { /* is colliding node out of its main position? */ + /* yes; move colliding node into free position */ + while (gnext(othern) != mp) othern = gnext(othern); /* find previous */ + gnext(othern) = n; /* redo the chain with `n' in place of `mp' */ + *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + gnext(mp) = NULL; /* now `mp' is free */ + setnilvalue(gval(mp)); + } + else { /* colliding node is in its own main position */ + /* new node will go into free position */ + gnext(n) = gnext(mp); /* chain new position */ + gnext(mp) = n; + mp = n; + } + } + setobj2t(L, gkey(mp), key); + luaC_barriert(L, t, key); + lua_assert(ttisnil(gval(mp))); + return gval(mp); +} + + +/* +** search function for integers +*/ +const TValue *ICACHE_FLASH_ATTR luaH_getnum (Table *t, int key) { + /* (1 <= key && key <= t->sizearray) */ + if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) + return &t->array[key-1]; + else { + lua_Number nk = cast_num(key); + Node *n = hashnum(t, nk); + do { /* check whether `key' is somewhere in the chain */ + if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk)) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; + } +} + +/* same thing for rotables */ +const TValue *ICACHE_FLASH_ATTR luaH_getnum_ro (void *t, int key) { + const TValue *res = luaR_findentry(t, NULL, key, NULL); + return res ? res : luaO_nilobject; +} + + +/* +** search function for strings +*/ +const TValue *ICACHE_FLASH_ATTR luaH_getstr (Table *t, TString *key) { + Node *n = hashstr(t, key); + do { /* check whether `key' is somewhere in the chain */ + if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; +} + +/* same thing for rotables */ +const TValue *ICACHE_FLASH_ATTR luaH_getstr_ro (void *t, TString *key) { + char keyname[LUA_MAX_ROTABLE_NAME + 1]; + const TValue *res; + if (!t) + return luaO_nilobject; + luaR_getcstr(keyname, key, LUA_MAX_ROTABLE_NAME); + res = luaR_findentry(t, keyname, 0, NULL); + return res ? res : luaO_nilobject; +} + + +/* +** main search function +*/ +const TValue *ICACHE_FLASH_ATTR luaH_get (Table *t, const TValue *key) { + switch (ttype(key)) { + case LUA_TNIL: return luaO_nilobject; + case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key)); + case LUA_TNUMBER: { + int k; + lua_Number n = nvalue(key); + lua_number2int(k, n); + if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */ + return luaH_getnum(t, k); /* use specialized version */ + /* else go through */ + } + default: { + Node *n = mainposition(t, key); + do { /* check whether `key' is somewhere in the chain */ + if (luaO_rawequalObj(key2tval(n), key)) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; + } + } +} + +/* same thing for rotables */ +const TValue *ICACHE_FLASH_ATTR luaH_get_ro (void *t, const TValue *key) { + switch (ttype(key)) { + case LUA_TNIL: return luaO_nilobject; + case LUA_TSTRING: return luaH_getstr_ro(t, rawtsvalue(key)); + case LUA_TNUMBER: { + int k; + lua_Number n = nvalue(key); + lua_number2int(k, n); + if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */ + return luaH_getnum_ro(t, k); /* use specialized version */ + /* else go through */ + } + default: { + return luaO_nilobject; + } + } +} + + +TValue *ICACHE_FLASH_ATTR luaH_set (lua_State *L, Table *t, const TValue *key) { + const TValue *p = luaH_get(t, key); + t->flags = 0; + if (p != luaO_nilobject) + return cast(TValue *, p); + else { + if (ttisnil(key)) luaG_runerror(L, "table index is nil"); + else if (ttisnumber(key) && luai_numisnan(nvalue(key))) + luaG_runerror(L, "table index is NaN"); + return newkey(L, t, key); + } +} + + +TValue *ICACHE_FLASH_ATTR luaH_setnum (lua_State *L, Table *t, int key) { + const TValue *p = luaH_getnum(t, key); + if (p != luaO_nilobject) + return cast(TValue *, p); + else { + TValue k; + setnvalue(&k, cast_num(key)); + return newkey(L, t, &k); + } +} + + +TValue *ICACHE_FLASH_ATTR luaH_setstr (lua_State *L, Table *t, TString *key) { + const TValue *p = luaH_getstr(t, key); + if (p != luaO_nilobject) + return cast(TValue *, p); + else { + TValue k; + setsvalue(L, &k, key); + return newkey(L, t, &k); + } +} + + +static int ICACHE_FLASH_ATTR unbound_search (Table *t, unsigned int j) { + unsigned int i = j; /* i is zero or a present index */ + j++; + /* find `i' and `j' such that i is present and j is not */ + while (!ttisnil(luaH_getnum(t, j))) { + i = j; + j *= 2; + if (j > cast(unsigned int, MAX_INT)) { /* overflow? */ + /* table was built with bad purposes: resort to linear search */ + i = 1; + while (!ttisnil(luaH_getnum(t, i))) i++; + return i - 1; + } + } + /* now do a binary search between them */ + while (j - i > 1) { + unsigned int m = (i+j)/2; + if (ttisnil(luaH_getnum(t, m))) j = m; + else i = m; + } + return i; +} + + +/* +** Try to find a boundary in table `t'. A `boundary' is an integer index +** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). +*/ +int ICACHE_FLASH_ATTR luaH_getn (Table *t) { + unsigned int j = t->sizearray; + if (j > 0 && ttisnil(&t->array[j - 1])) { + /* there is a boundary in the array part: (binary) search for it */ + unsigned int i = 0; + while (j - i > 1) { + unsigned int m = (i+j)/2; + if (ttisnil(&t->array[m - 1])) j = m; + else i = m; + } + return i; + } + /* else must find a boundary in hash part */ + else if (t->node == dummynode) /* hash part is empty? */ + return j; /* that is easy... */ + else return unbound_search(t, j); +} + +/* same thing for rotables */ +int ICACHE_FLASH_ATTR luaH_getn_ro (void *t) { + int i = 1, len=0; + + while(luaR_findentry(t, NULL, i ++, NULL)) + len ++; + return len; +} + +#if defined(LUA_DEBUG) + +Node *ICACHE_FLASH_ATTR luaH_mainposition (const Table *t, const TValue *key) { + return mainposition(t, key); +} + +int ICACHE_FLASH_ATTR luaH_isdummy (Node *n) { return n == dummynode; } + +#endif diff --git a/app/lua/ltable.h b/app/lua/ltable.h new file mode 100644 index 00000000..4835f2c3 --- /dev/null +++ b/app/lua/ltable.h @@ -0,0 +1,44 @@ +/* +** $Id: ltable.h,v 2.10.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + +#ifndef ltable_h +#define ltable_h + +#include "lobject.h" + + +#define gnode(t,i) (&(t)->node[i]) +#define gkey(n) (&(n)->i_key.tvk) +#define gval(n) (&(n)->i_val) +#define gnext(n) ((n)->i_key.nk.next) + +#define key2tval(n) (&(n)->i_key.tvk) + + +LUAI_FUNC const TValue *luaH_getnum (Table *t, int key); +LUAI_FUNC const TValue *luaH_getnum_ro (void *t, int key); +LUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key); +LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); +LUAI_FUNC const TValue *luaH_getstr_ro (void *t, TString *key); +LUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key); +LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); +LUAI_FUNC const TValue *luaH_get_ro (void *t, const TValue *key); +LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key); +LUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash); +LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize); +LUAI_FUNC void luaH_free (lua_State *L, Table *t); +LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); +LUAI_FUNC int luaH_next_ro (lua_State *L, void *t, StkId key); +LUAI_FUNC int luaH_getn (Table *t); +LUAI_FUNC int luaH_getn_ro (void *t); + +#if defined(LUA_DEBUG) +LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); +LUAI_FUNC int luaH_isdummy (Node *n); +#endif + + +#endif diff --git a/app/lua/ltablib.c b/app/lua/ltablib.c new file mode 100644 index 00000000..08e0d498 --- /dev/null +++ b/app/lua/ltablib.c @@ -0,0 +1,287 @@ +/* +** $Id: ltablib.c,v 1.38.1.3 2008/02/14 16:46:58 roberto Exp $ +** Library for Table Manipulation +** See Copyright Notice in lua.h +*/ + + +#include "c_stddef.h" + +#define ltablib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "lrotable.h" + + +#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) + + +static int ICACHE_FLASH_ATTR foreachi (lua_State *L) { + int i; + int n = aux_getn(L, 1); + luaL_checkanyfunction(L, 2); + for (i=1; i <= n; i++) { + lua_pushvalue(L, 2); /* function */ + lua_pushinteger(L, i); /* 1st argument */ + lua_rawgeti(L, 1, i); /* 2nd argument */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 1); /* remove nil result */ + } + return 0; +} + + +static int ICACHE_FLASH_ATTR foreach (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkanyfunction(L, 2); + lua_pushnil(L); /* first key */ + while (lua_next(L, 1)) { + lua_pushvalue(L, 2); /* function */ + lua_pushvalue(L, -3); /* key */ + lua_pushvalue(L, -3); /* value */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 2); /* remove value and result */ + } + return 0; +} + + +static int ICACHE_FLASH_ATTR maxn (lua_State *L) { + lua_Number max = 0; + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushnil(L); /* first key */ + while (lua_next(L, 1)) { + lua_pop(L, 1); /* remove value */ + if (lua_type(L, -1) == LUA_TNUMBER) { + lua_Number v = lua_tonumber(L, -1); + if (v > max) max = v; + } + } + lua_pushnumber(L, max); + return 1; +} + + +static int ICACHE_FLASH_ATTR getn (lua_State *L) { + lua_pushinteger(L, aux_getn(L, 1)); + return 1; +} + + +static int ICACHE_FLASH_ATTR setn (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); +#ifndef luaL_setn + luaL_setn(L, 1, luaL_checkint(L, 2)); +#else + luaL_error(L, LUA_QL("setn") " is obsolete"); +#endif + lua_pushvalue(L, 1); + return 1; +} + + +static int ICACHE_FLASH_ATTR tinsert (lua_State *L) { + int e = aux_getn(L, 1) + 1; /* first empty element */ + int pos; /* where to insert new element */ + switch (lua_gettop(L)) { + case 2: { /* called with only 2 arguments */ + pos = e; /* insert new element at the end */ + break; + } + case 3: { + int i; + pos = luaL_checkint(L, 2); /* 2nd argument is the position */ + if (pos > e) e = pos; /* `grow' array if necessary */ + for (i = e; i > pos; i--) { /* move up elements */ + lua_rawgeti(L, 1, i-1); + lua_rawseti(L, 1, i); /* t[i] = t[i-1] */ + } + break; + } + default: { + return luaL_error(L, "wrong number of arguments to " LUA_QL("insert")); + } + } + luaL_setn(L, 1, e); /* new size */ + lua_rawseti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int ICACHE_FLASH_ATTR tremove (lua_State *L) { + int e = aux_getn(L, 1); + int pos = luaL_optint(L, 2, e); + if (!(1 <= pos && pos <= e)) /* position is outside bounds? */ + return 0; /* nothing to remove */ + luaL_setn(L, 1, e - 1); /* t.n = n-1 */ + lua_rawgeti(L, 1, pos); /* result = t[pos] */ + for ( ;pos= P */ + while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i>u) luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* repeat --j until a[j] <= P */ + while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { + if (jtmname[i] = luaS_new(L, luaT_eventname[i]); + luaS_fix(G(L)->tmname[i]); /* never collect these names */ + } +} + + +/* +** function to be used with macro "fasttm": optimized for absence of +** tag methods +*/ +const TValue *ICACHE_FLASH_ATTR luaT_gettm (Table *events, TMS event, TString *ename) { + const TValue *tm = luaR_isrotable(events) ? luaH_getstr_ro(events, ename) : luaH_getstr(events, ename); + lua_assert(event <= TM_EQ); + if (ttisnil(tm)) { /* no tag method? */ + if (!luaR_isrotable(events)) + events->flags |= cast_byte(1u<metatable; + break; + case LUA_TROTABLE: + mt = (Table*)luaR_getmeta(rvalue(o)); + break; + case LUA_TUSERDATA: + mt = uvalue(o)->metatable; + break; + default: + mt = G(L)->mt[ttype(o)]; + } + if (!mt) + return luaO_nilobject; + else if (luaR_isrotable(mt)) + return luaH_getstr_ro(mt, G(L)->tmname[event]); + else + return luaH_getstr(mt, G(L)->tmname[event]); +} diff --git a/app/lua/ltm.h b/app/lua/ltm.h new file mode 100644 index 00000000..662fa2ae --- /dev/null +++ b/app/lua/ltm.h @@ -0,0 +1,54 @@ +/* +** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $ +** Tag methods +** See Copyright Notice in lua.h +*/ + +#ifndef ltm_h +#define ltm_h + + +#include "lobject.h" + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER TM" +*/ +typedef enum { + TM_INDEX, + TM_NEWINDEX, + TM_GC, + TM_MODE, + TM_EQ, /* last tag method with `fast' access */ + TM_ADD, + TM_SUB, + TM_MUL, + TM_DIV, + TM_MOD, + TM_POW, + TM_UNM, + TM_LEN, + TM_LT, + TM_LE, + TM_CONCAT, + TM_CALL, + TM_N /* number of elements in the enum */ +} TMS; + + + +#define gfasttm(g,et,e) ((et) == NULL ? NULL : \ + !luaR_isrotable(et) && ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) + +#define fasttm(l,et,e) gfasttm(G(l), et, e) + +LUAI_DATA const char *const luaT_typenames[]; + + +LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); +LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, + TMS event); +LUAI_FUNC void luaT_init (lua_State *L); + +#endif diff --git a/app/lua/lua.c b/app/lua/lua.c new file mode 100644 index 00000000..6eab7724 --- /dev/null +++ b/app/lua/lua.c @@ -0,0 +1,699 @@ +/* +** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $ +** Lua stand-alone interpreter +** See Copyright Notice in lua.h +*/ + + +// #include "c_signal.h" +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_string.h" +#include "flash_fs.h" + +#define lua_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "legc.h" + +#include "os_type.h" + +os_timer_t lua_timer; +LOCAL os_timer_t readline_timer; + +lua_State *globalL = NULL; + +lua_Load gLoad; + +static const char *progname = LUA_PROGNAME; + +static int key_press_count = 0; +int led_high_count = LED_HIGH_COUNT_DEFAULT; +int led_low_count = LED_LOW_COUNT_DEFAULT; +static int led_count = 0; +#if 0 +static void ICACHE_FLASH_ATTR lstop (lua_State *L, lua_Debug *ar) { + (void)ar; /* unused arg. */ + lua_sethook(L, NULL, 0, 0); + luaL_error(L, "interrupted!"); +} + + +static void ICACHE_FLASH_ATTR laction (int i) { + // signal(i, SIG_DFL); + /* if another SIGINT happens before lstop, + terminate process (default action) */ + lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); +} + + +static void ICACHE_FLASH_ATTR print_usage (void) { +#if defined(LUA_USE_STDIO) + c_fprintf(c_stderr, +#else + luai_writestringerror( +#endif + "usage: %s [options] [script [args]].\n" + "Available options are:\n" + " -e stat execute string " LUA_QL("stat") "\n" + " -l name require library " LUA_QL("name") "\n" + " -m limit set memory limit. (units are in Kbytes)\n" + " -i enter interactive mode after executing " LUA_QL("script") "\n" + " -v show version information\n" + " -- stop handling options\n" + " - execute stdin and stop handling options\n" + , + progname); +#if defined(LUA_USE_STDIO) + c_fflush(c_stderr); +#endif +} +#endif + +static void ICACHE_FLASH_ATTR l_message (const char *pname, const char *msg) { +#if defined(LUA_USE_STDIO) + if (pname) c_fprintf(c_stderr, "%s: ", pname); + c_fprintf(c_stderr, "%s\n", msg); + c_fflush(c_stderr); +#else + if (pname) luai_writestringerror("%s: ", pname); + luai_writestringerror("%s\n", msg); +#endif +} + + +static int ICACHE_FLASH_ATTR report (lua_State *L, int status) { + if (status && !lua_isnil(L, -1)) { + const char *msg = lua_tostring(L, -1); + if (msg == NULL) msg = "(error object is not a string)"; + l_message(progname, msg); + lua_pop(L, 1); + } + return status; +} + + +static int ICACHE_FLASH_ATTR traceback (lua_State *L) { + if (!lua_isstring(L, 1)) /* 'message' not a string? */ + return 1; /* keep it intact */ + lua_getfield(L, LUA_GLOBALSINDEX, "debug"); + if (!lua_istable(L, -1) && !lua_isrotable(L, -1)) { + lua_pop(L, 1); + return 1; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1) && !lua_islightfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ + return 1; +} + + +static int ICACHE_FLASH_ATTR docall (lua_State *L, int narg, int clear) { + int status; + int base = lua_gettop(L) - narg; /* function index */ + lua_pushcfunction(L, traceback); /* push traceback function */ + lua_insert(L, base); /* put it under chunk and args */ + // signal(SIGINT, laction); + status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base); + // signal(SIGINT, SIG_DFL); + lua_remove(L, base); /* remove traceback function */ + /* force a complete garbage collection in case of errors */ + if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); + return status; +} + + +static void ICACHE_FLASH_ATTR print_version (void) { + // l_message(NULL, LUA_RELEASE " " LUA_COPYRIGHT); + l_message(NULL, NODE_VERSION " " BUILD_DATE " powered by " LUA_RELEASE); +} + + +static int ICACHE_FLASH_ATTR getargs (lua_State *L, char **argv, int n) { + int narg; + int i; + int argc = 0; + while (argv[argc]) argc++; /* count total number of arguments */ + narg = argc - (n + 1); /* number of arguments to the script */ + luaL_checkstack(L, narg + 3, "too many arguments to script"); + for (i=n+1; i < argc; i++) + lua_pushstring(L, argv[i]); + lua_createtable(L, narg, n + 1); + for (i=0; i < argc; i++) { + lua_pushstring(L, argv[i]); + lua_rawseti(L, -2, i - n); + } + return narg; +} + +#if 0 +static int ICACHE_FLASH_ATTR dofile (lua_State *L, const char *name) { + int status = luaL_loadfile(L, name) || docall(L, 0, 1); + return report(L, status); +} +#else +static int ICACHE_FLASH_ATTR dofsfile (lua_State *L, const char *name) { + int status = luaL_loadfsfile(L, name) || docall(L, 0, 1); + return report(L, status); +} +#endif + +static int ICACHE_FLASH_ATTR dostring (lua_State *L, const char *s, const char *name) { + int status = luaL_loadbuffer(L, s, c_strlen(s), name) || docall(L, 0, 1); + return report(L, status); +} + + +static int ICACHE_FLASH_ATTR dolibrary (lua_State *L, const char *name) { + lua_getglobal(L, "require"); + lua_pushstring(L, name); + return report(L, docall(L, 1, 1)); +} + +static const char *ICACHE_FLASH_ATTR get_prompt (lua_State *L, int firstline) { + const char *p; + lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2"); + p = lua_tostring(L, -1); + if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); + lua_pop(L, 1); /* remove global */ + return p; +} + + +static int ICACHE_FLASH_ATTR incomplete (lua_State *L, int status) { + if (status == LUA_ERRSYNTAX) { + size_t lmsg; + const char *msg = lua_tolstring(L, -1, &lmsg); + const char *tp = msg + lmsg - (sizeof(LUA_QL("")) - 1); + if (c_strstr(msg, LUA_QL("")) == tp) { + lua_pop(L, 1); + return 1; + } + } + return 0; /* else... */ +} + +#if 0 +static int ICACHE_FLASH_ATTR pushline (lua_State *L, int firstline) { + char buffer[LUA_MAXINPUT]; + char *b = buffer; + size_t l; + const char *prmt = get_prompt(L, firstline); + if (lua_readline(L, b, prmt) == 0) + return 0; /* no input */ + l = c_strlen(b); + if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ + b[l-1] = '\0'; /* remove it */ + if (firstline && b[0] == '=') /* first line starts with `=' ? */ + lua_pushfstring(L, "return %s", b+1); /* change it to `return' */ + else + lua_pushstring(L, b); + lua_freeline(L, b); + return 1; +} + + +static int ICACHE_FLASH_ATTR loadline (lua_State *L) { + int status; + lua_settop(L, 0); + if (!pushline(L, 1)) + return -1; /* no input */ + for (;;) { /* repeat until gets a complete line */ + status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin"); + if (!incomplete(L, status)) break; /* cannot try to add lines? */ + if (!pushline(L, 0)) /* no more input? */ + return -1; + lua_pushliteral(L, "\n"); /* add a new line... */ + lua_insert(L, -2); /* ...between the two lines */ + lua_concat(L, 3); /* join them */ + } + lua_saveline(L, 1); + lua_remove(L, 1); /* remove line */ + return status; +} + + +static void ICACHE_FLASH_ATTR dotty (lua_State *L) { + int status; + const char *oldprogname = progname; + progname = NULL; + while ((status = loadline(L)) != -1) { + if (status == 0) status = docall(L, 0, 0); + report(L, status); + if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */ + lua_getglobal(L, "print"); + lua_insert(L, 1); + if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0) + l_message(progname, lua_pushfstring(L, + "error calling " LUA_QL("print") " (%s)", + lua_tostring(L, -1))); + } + } + lua_settop(L, 0); /* clear stack */ + +#if defined(LUA_USE_STDIO) + c_fputs("\n", c_stdout); + c_fflush(c_stdout); +#else + luai_writeline(); +#endif + + progname = oldprogname; +} + + +static int ICACHE_FLASH_ATTR handle_script (lua_State *L, char **argv, int n) { + int status; + const char *fname; + int narg = getargs(L, argv, n); /* collect arguments */ + lua_setglobal(L, "arg"); + fname = argv[n]; + if (c_strcmp(fname, "-") == 0 && c_strcmp(argv[n-1], "--") != 0) + fname = NULL; /* stdin */ + status = luaL_loadfile(L, fname); + lua_insert(L, -(narg+1)); + if (status == 0) + status = docall(L, narg, 0); + else + lua_pop(L, narg); + return report(L, status); +} +#endif + +/* check that argument has no extra characters at the end */ +#define notail(x) {if ((x)[2] != '\0') return -1;} + + +static int ICACHE_FLASH_ATTR collectargs (char **argv, int *pi, int *pv, int *pe) { + int i; + for (i = 1; argv[i] != NULL; i++) { + if (argv[i][0] != '-') /* not an option? */ + return i; + switch (argv[i][1]) { /* option */ + case '-': + notail(argv[i]); + return (argv[i+1] != NULL ? i+1 : 0); + case '\0': + return i; + case 'i': + notail(argv[i]); + *pi = 1; /* go through */ + case 'v': + notail(argv[i]); + *pv = 1; + break; + case 'e': + *pe = 1; /* go through */ + case 'm': /* go through */ + case 'l': + if (argv[i][2] == '\0') { + i++; + if (argv[i] == NULL) return -1; + } + break; + default: return -1; /* invalid option */ + } + } + return 0; +} + + +static int ICACHE_FLASH_ATTR runargs (lua_State *L, char **argv, int n) { + int i; + for (i = 1; i < n; i++) { + if (argv[i] == NULL) continue; + lua_assert(argv[i][0] == '-'); + switch (argv[i][1]) { /* option */ + case 'e': { + const char *chunk = argv[i] + 2; + if (*chunk == '\0') chunk = argv[++i]; + lua_assert(chunk != NULL); + if (dostring(L, chunk, "=(command line)") != 0) + return 1; + break; + } + case 'm': { + const char *limit = argv[i] + 2; + int memlimit=0; + if (*limit == '\0') limit = argv[++i]; + lua_assert(limit != NULL); + memlimit = c_atoi(limit); + lua_gc(L, LUA_GCSETMEMLIMIT, memlimit); + break; + } + case 'l': { + const char *filename = argv[i] + 2; + if (*filename == '\0') filename = argv[++i]; + lua_assert(filename != NULL); + if (dolibrary(L, filename)) + return 1; /* stop if file fails */ + break; + } + default: break; + } + } + return 0; +} + + +static int ICACHE_FLASH_ATTR handle_luainit (lua_State *L) { + const char *init = c_getenv(LUA_INIT); + if (init == NULL) return 0; /* status OK */ + else if (init[0] == '@') +#if 0 + return dofile(L, init+1); +#else + return dofsfile(L, init+1); +#endif + else + return dostring(L, init, "=" LUA_INIT); +} + + +struct Smain { + int argc; + char **argv; + int status; +}; + + +static int ICACHE_FLASH_ATTR pmain (lua_State *L) { + struct Smain *s = (struct Smain *)lua_touserdata(L, 1); + char **argv = s->argv; + int script; + int has_i = 0, has_v = 0, has_e = 0; + globalL = L; + if (argv[0] && argv[0][0]) progname = argv[0]; + lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */ + luaL_openlibs(L); /* open libraries */ + lua_gc(L, LUA_GCRESTART, 0); + print_version(); + s->status = handle_luainit(L); +#if 0 + if (s->status != 0) return 0; +#endif + script = collectargs(argv, &has_i, &has_v, &has_e); + if (script < 0) { /* invalid args? */ +#if 0 + print_usage(); +#endif + s->status = 1; + return 0; + } + // if (has_v) print_version(); + s->status = runargs(L, argv, (script > 0) ? script : s->argc); + if (s->status != 0) return 0; +#if 0 + if (script) + s->status = handle_script(L, argv, script); + if (s->status != 0) return 0; + if (has_i) + dotty(L); + else if (script == 0 && !has_e && !has_v) { + if (lua_stdin_is_tty()) { + print_version(); + dotty(L); + } + else dofile(L, NULL); /* executes stdin as a file */ + } +#endif + return 0; +} + +void dojob(lua_Load *load); +void readline(lua_Load *load); +char line_buffer[LUA_MAXINPUT]; + +#ifdef LUA_RPC +int ICACHE_FLASH_ATTR main (int argc, char **argv) { +#else +int ICACHE_FLASH_ATTR lua_main (int argc, char **argv) { +#endif + int status; + struct Smain s; + lua_State *L = lua_open(); /* create state */ + if (L == NULL) { + l_message(argv[0], "cannot create state: not enough memory"); + return EXIT_FAILURE; + } + s.argc = argc; + s.argv = argv; + status = lua_cpcall(L, &pmain, &s); + report(L, status); + + gLoad.L = L; + gLoad.firstline = 1; + gLoad.done = 0; + gLoad.line = line_buffer; + gLoad.len = LUA_MAXINPUT; + gLoad.line_position = 0; + gLoad.prmt = get_prompt(L, 1); + + // dojob(&gLoad); + os_timer_disarm(&lua_timer); + os_timer_setfn(&lua_timer, (os_timer_func_t *)dojob, &gLoad); + os_timer_arm(&lua_timer, READLINE_INTERVAL, 0); // no repeat + + NODE_DBG("Heap size::%d.\n",system_get_free_heap_size()); + legc_set_mode( L, EGC_ALWAYS, 4096 ); + // legc_set_mode( L, EGC_ON_MEM_LIMIT, 4096 ); + // lua_close(L); + return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS; +} + +void ICACHE_FLASH_ATTR donejob(lua_Load *load){ + lua_close(load->L); +} + +#if 0 +int log_fd = -1; +int noparse = 0; +#endif + +void ICACHE_FLASH_ATTR dojob(lua_Load *load){ + size_t l, rs; + int status; + char *b = load->line; + lua_State *L = load->L; + + const char *oldprogname = progname; + progname = NULL; + + do{ + if(load->done == 1){ + l = c_strlen(b); +#if 0 + if(log_fd!=-1 && l>0) + { + if(l>=10 && (c_strstr(b,"log.stop()")) ) + { + fs_close(log_fd); + log_fd = -1; + noparse = 0; + NODE_ERR( "log stopping.\n" ); + } + if(log_fd!=-1){ // test again + rs = fs_write(log_fd, b, l); + if(rs!=l){ + fs_close(log_fd); + log_fd = -1; + } else { + rs = fs_write(log_fd, "\r\n", 2); + if(rs!=2){ + fs_close(log_fd); + log_fd = -1; + } + } + } + if(noparse){ + break; + } + } +#endif + if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ + b[l-1] = '\0'; /* remove it */ + if (load->firstline && b[0] == '=') /* first line starts with `=' ? */ + lua_pushfstring(L, "return %s", b+1); /* change it to `return' */ + else + lua_pushstring(L, b); + if(load->firstline != 1){ + lua_pushliteral(L, "\n"); /* add a new line... */ + lua_insert(L, -2); /* ...between the two lines */ + lua_concat(L, 3); /* join them */ + } + + status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin"); + if (!incomplete(L, status)) { /* cannot try to add lines? */ + lua_remove(L, 1); /* remove line */ + if (status == 0) { + status = docall(L, 0, 0); + } + report(L, status); + if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */ + lua_getglobal(L, "print"); + lua_insert(L, 1); + if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0) + l_message(progname, lua_pushfstring(L, + "error calling " LUA_QL("print") " (%s)", + lua_tostring(L, -1))); + } + load->firstline = 1; + load->prmt = get_prompt(L, 1); + lua_settop(L, 0); + /* force a complete garbage collection in case of errors */ + if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); + } else { + load->firstline = 0; + load->prmt = get_prompt(L, 0); + } + } + }while(0); + + progname = oldprogname; + + load->done = 0; + load->line_position = 0; + c_memset(load->line, 0, load->len); + os_timer_disarm(&readline_timer); + os_timer_setfn(&readline_timer, (os_timer_func_t *)readline, load); + os_timer_arm(&readline_timer, READLINE_INTERVAL, 0); // no repeat + c_puts(load->prmt); + // NODE_DBG("dojob() is called with firstline.\n"); +} + +extern void key_long_press(void *arg); +extern void key_short_press(void *arg); +static bool key_short_pressed = false; +static bool key_long_pressed = false; +void ICACHE_FLASH_ATTR update_key_led(){ + uint8_t temp = 1, level = 1; + led_count++; + if(led_count>led_low_count+led_high_count){ + led_count = 0; // reset led_count, the level still high + } else if(led_count>led_low_count && led_count <=led_high_count+led_low_count){ + level = 1; // output high level + } else if(led_count<=led_low_count){ + level = 0; // output low level + } + temp = platform_key_led(level); + if(temp == 0){ // key is pressed + key_press_count++; + if(key_press_count>=KEY_LONG_COUNT){ + // key_long_press(NULL); + key_long_pressed = true; + key_short_pressed = false; + // key_press_count = 0; + } else if(key_press_count>=KEY_SHORT_COUNT){ // < KEY_LONG_COUNT + // key_short_press(NULL); + key_short_pressed = true; + } + }else{ // key is released + key_press_count = 0; + if(key_long_pressed){ + key_long_press(NULL); + key_long_pressed = false; + } + if(key_short_pressed){ + key_short_press(NULL); + key_short_pressed = false; + } + } +} + +#ifndef uart_putc +#define uart_putc uart0_putc +#endif +extern bool uart_on_data_cb(const char *buf, size_t len); +extern bool uart0_echo; +void ICACHE_FLASH_ATTR readline(lua_Load *load){ + // NODE_DBG("readline() is called.\n"); + update_key_led(); + char ch; + while ((ch = uart_getc()) != 0) + { + /* handle CR key */ + if (ch == '\r') + { + char next; + if ((next = uart_getc()) != 0) + ch = next; + } + /* backspace key */ + else if (ch == 0x7f || ch == 0x08) + { + if (load->line_position > 0) + { + if(uart0_echo) uart_putc(0x08); + if(uart0_echo) uart_putc(' '); + if(uart0_echo) uart_putc(0x08); + load->line_position--; + } + load->line[load->line_position] = 0; + continue; + } + /* EOF(ctrl+d) */ + else if (ch == 0x04) + { + if (load->line_position == 0) + // No input which makes lua interpreter close + donejob(load); + else + continue; + } + /* other control character or not an acsii character */ + else if (ch < 0x20 || ch >= 0x80) + { + continue; + } + /* end of line */ + if (ch == '\r' || ch == '\n') + { + load->line[load->line_position] = 0; + if(uart0_echo) uart_putc('\n'); + if(uart_on_data_cb(load->line, load->line_position)){ + load->line_position = 0; + } + if (load->line_position == 0) + { + /* Get a empty line, then go to get a new line */ + c_puts(load->prmt); + os_timer_disarm(&readline_timer); + os_timer_setfn(&readline_timer, (os_timer_func_t *)readline, load); + os_timer_arm(&readline_timer, READLINE_INTERVAL, 0); // no repeat + } else { + load->done = 1; + os_timer_disarm(&lua_timer); + os_timer_setfn(&lua_timer, (os_timer_func_t *)dojob, load); + os_timer_arm(&lua_timer, READLINE_INTERVAL, 0); // no repeat + } + } + + /* echo */ + if(uart0_echo) uart_putc(ch); + load->line[load->line_position] = ch; + ch = 0; + load->line_position++; + + /* it's a large line, discard it */ + if (load->line_position >= load->len) + load->line_position = 0; + } + // if there is no input from user, repeat readline() + os_timer_disarm(&readline_timer); + os_timer_setfn(&readline_timer, (os_timer_func_t *)readline, load); + os_timer_arm(&readline_timer, READLINE_INTERVAL, 0); // no repeat +} + +void ICACHE_FLASH_ATTR dogc(void){ + if(globalL){ + lua_gc(globalL, LUA_GCCOLLECT, 0); + } +} diff --git a/app/lua/lua.h b/app/lua/lua.h new file mode 100644 index 00000000..8f883f6f --- /dev/null +++ b/app/lua/lua.h @@ -0,0 +1,409 @@ +/* +** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 roberto Exp $ +** Lua - An Extensible Extension Language +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include "c_stdarg.h" +#include "c_stddef.h" +#include "c_types.h" + +#include "luaconf.h" + + +#define LUA_VERSION "Lua 5.1" +#define LUA_RELEASE "Lua 5.1.4" +#define LUA_VERSION_NUM 501 +#define LUA_COPYRIGHT "Copyright (C) 1994-2011 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes" + + +/* mark for precompiled code (`Lua') */ +#define LUA_SIGNATURE "\033Lua" + +/* option for multiple returns in `lua_pcall' and `lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** pseudo-indices +*/ +#define LUA_REGISTRYINDEX (-10000) +#define LUA_ENVIRONINDEX (-10001) +#define LUA_GLOBALSINDEX (-10002) +#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) + + +/* thread status; 0 is OK */ +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 + + +typedef struct lua_State lua_State; + +typedef int (*lua_CFunction) (lua_State *L); + + +/* +** functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); + + +/* +** prototype for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TROTABLE 2 +#define LUA_TLIGHTFUNCTION 3 +#define LUA_TLIGHTUSERDATA 4 +#define LUA_TNUMBER 5 +#define LUA_TSTRING 6 +#define LUA_TTABLE 7 +#define LUA_TFUNCTION 8 +#define LUA_TUSERDATA 9 +#define LUA_TTHREAD 10 + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_remove) (lua_State *L, int idx); +LUA_API void (lua_insert) (lua_State *L, int idx); +LUA_API void (lua_replace) (lua_State *L, int idx); +LUA_API int (lua_checkstack) (lua_State *L, int sz); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2); + +LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx); +LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API size_t (lua_objlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l); +LUA_API void (lua_pushrolstring) (lua_State *L, const char *s, size_t l); +LUA_API void (lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API void (lua_pushlightfunction) (lua_State *L, void *p); +LUA_API void (lua_pushrotable) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API void (lua_gettable) (lua_State *L, int idx); +LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawget) (lua_State *L, int idx); +LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n); +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API void (lua_getfenv) (lua_State *L, int idx); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, int n); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API int (lua_setfenv) (lua_State *L, int idx); + + +/* +** `load' and `call' functions (load and run Lua code) +*/ +LUA_API void (lua_call) (lua_State *L, int nargs, int nresults); +LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); +LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud); +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yield) (lua_State *L, int nresults); +LUA_API int (lua_resume) (lua_State *L, int narg); +LUA_API int (lua_status) (lua_State *L); + +/* +** garbage-collection function and options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 +#define LUA_GCSETMEMLIMIT 8 +#define LUA_GCGETMEMLIMIT 9 + +LUA_API int (lua_gc) (lua_State *L, int what, int data); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_strlen(L,i) lua_objlen(L, (i)) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_islightfunction(L,n) (lua_type(L, (n)) == LUA_TLIGHTFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_isrotable(L,n) (lua_type(L, (n)) == LUA_TROTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) \ + lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) + +#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) +#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + + +/* +** compatibility macros and functions +*/ + +// BogdanM: modified for eLua interrupt support +//#define lua_open() luaL_newstate() +lua_State* lua_open(void); +lua_State* lua_getstate(void); + +#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX) + +#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0) + +#define lua_Chunkreader lua_Reader +#define lua_Chunkwriter lua_Writer + + +/* hack */ +LUA_API void lua_setlevel (lua_State *from, lua_State *to); + + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILRET 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + +typedef struct lua_Debug lua_Debug; /* activation record */ + + +/* Functions to be called by the debuger in specific events */ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n); +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n); + +LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook lua_gethook (lua_State *L); +LUA_API int lua_gethookmask (lua_State *L); +LUA_API int lua_gethookcount (lua_State *L); + + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) `global', `local', `field', `method' */ + const char *what; /* (S) `Lua', `C', `main', `tail' */ + const char *source; /* (S) */ + int currentline; /* (l) */ + int nups; /* (u) number of upvalues */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + int i_ci; /* active function */ +}; + +/* }====================================================================== */ + +typedef struct __lua_load{ + lua_State *L; + int firstline; + char *line; + int line_position; + size_t len; + int done; + const char *prmt; +}lua_Load; + +int lua_main( int argc, char **argv ); + +/****************************************************************************** +* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved. +* +* 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. +******************************************************************************/ + + +#endif diff --git a/app/lua/luaconf.h b/app/lua/luaconf.h new file mode 100644 index 00000000..90da40dc --- /dev/null +++ b/app/lua/luaconf.h @@ -0,0 +1,888 @@ +/* +** $Id: luaconf.h,v 1.82.1.7 2008/02/11 16:25:08 roberto Exp $ +** Configuration file for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef lconfig_h +#define lconfig_h + +#include "c_limits.h" +#include "c_stddef.h" +#include "user_config.h" + +/* +** ================================================================== +** Search for "@@" to find all configurable definitions. +** =================================================================== +*/ + + +/* +@@ LUA_ANSI controls the use of non-ansi features. +** CHANGE it (define it) if you want Lua to avoid the use of any +** non-ansi feature or library. +*/ +#if defined(__STRICT_ANSI__) +#define LUA_ANSI +#endif + + +#if !defined(LUA_ANSI) && defined(_WIN32) +#define LUA_WIN +#endif + +#if defined(LUA_USE_LINUX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#define LUA_USE_READLINE /* needs some extra libraries */ +#endif + +#if defined(LUA_USE_MACOSX) +#define LUA_USE_POSIX +#define LUA_DL_DYLD /* does not need extra library */ +#endif + + + +/* +@@ LUA_USE_POSIX includes all functionallity listed as X/Open System +@* Interfaces Extension (XSI). +** CHANGE it (define it) if your system is XSI compatible. +*/ +#if defined(LUA_USE_POSIX) +#define LUA_USE_MKSTEMP +#define LUA_USE_ISATTY +#define LUA_USE_POPEN +#define LUA_USE_ULONGJMP +#endif + + +/* +@@ LUA_PATH and LUA_CPATH are the names of the environment variables that +@* Lua check to set its paths. +@@ LUA_INIT is the name of the environment variable that Lua +@* checks for initialization code. +** CHANGE them if you want different names. +*/ +#define LUA_PATH "LUA_PATH" +#define LUA_CPATH "LUA_CPATH" +#define LUA_INIT "LUA_INIT" + + +/* +@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for +@* Lua libraries. +@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for +@* C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ +#if defined(_WIN32) +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" + +//## Modified for eLua +//## Defaults search modules path to our ROM File System +#ifndef LUA_RPC +#define LUA_PATH_DEFAULT "/rfs/?.lua;/rfs/?.lc;/mmc/?.lua;/mmc/?.lc;/rom/?.lua;/rom/?.lc" +#define LUA_CPATH_DEFAULT "" +#else // #ifndef LUA_RPC +#define LUA_PATH_DEFAULT \ + ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua" +#define LUA_CPATH_DEFAULT \ + ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" +#endif // #ifndef LUA_RPC + +#else // #if defined(_WIN32) + +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/5.1/" +#define LUA_CDIR LUA_ROOT "lib/lua/5.1/" + +#ifndef LUA_RPC +#define LUA_PATH_DEFAULT "?.lua;?.lc" +#define LUA_CPATH_DEFAULT "" +#else // #ifndef LUA_RPC +#define LUA_PATH_DEFAULT \ + "./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua" +#define LUA_CPATH_DEFAULT \ + "./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so" +#endif // #ifndef LUA_RPC + +#endif // #if defined(_WIN32) + + +/* +@@ LUA_DIRSEP is the directory separator (for submodules). +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Lua automatically uses "\".) +*/ +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif + + +/* +@@ LUA_PATHSEP is the character that separates templates in a path. +@@ LUA_PATH_MARK is the string that marks the substitution points in a +@* template. +@@ LUA_EXECDIR in a Windows path is replaced by the executable's +@* directory. +@@ LUA_IGMARK is a mark to ignore all before it when bulding the +@* luaopen_ function name. +** CHANGE them if for some reason your system cannot use those +** characters. (E.g., if one of those characters is a common character +** in file/directory names.) Probably you do not need to change them. +*/ +#define LUA_PATHSEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXECDIR "!" +#define LUA_IGMARK "-" + + +/* +@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger. +** CHANGE that if ptrdiff_t is not adequate on your machine. (On most +** machines, ptrdiff_t gives a good choice between int or long.) +*/ + +/* Changed to long for use with integral Lua numbers. */ +#if !defined LUA_NUMBER_INTEGRAL +#define LUA_INTEGER ptrdiff_t +#else + #if !defined LUA_INTEGRAL_LONGLONG + #define LUA_INTEGER long + #else + #define LUA_INTEGER long long + #endif // #if !defined LUA_INTEGRAL_LONGLONG +#endif // #if !defined LUA_NUMBER_INTEGRAL + +/* +@@ LUA_API is a mark for all core API functions. +@@ LUALIB_API is a mark for all standard library functions. +** CHANGE them if you need to define those functions in some special way. +** For instance, if you want to create one Windows DLL with the core and +** the libraries, you may want to use the following definition (define +** LUA_BUILD_AS_DLL to get it). +*/ +#if defined(LUA_BUILD_AS_DLL) + +#if defined(LUA_CORE) || defined(LUA_LIB) +#define LUA_API __declspec(dllexport) +#else +#define LUA_API __declspec(dllimport) +#endif + +#else + +#define LUA_API extern + +#endif + +/* more often than not the libs go together with the core */ +#define LUALIB_API LUA_API + + +/* +@@ LUAI_FUNC is a mark for all extern functions that are not to be +@* exported to outside modules. +@@ LUAI_DATA is a mark for all extern (const) variables that are not to +@* be exported to outside modules. +** CHANGE them if you need to mark them in some special way. Elf/gcc +** (versions 3.2 and later) mark them as "hidden" to optimize access +** when Lua is compiled as a shared library. +*/ +#if defined(luaall_c) +#define LUAI_FUNC static +#define LUAI_DATA /* empty */ + +#elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + defined(__ELF__) +#define LUAI_FUNC __attribute__((visibility("hidden"))) extern +#define LUAI_DATA LUAI_FUNC + +#else +#define LUAI_FUNC extern +#define LUAI_DATA extern +#endif + + + +/* +@@ LUA_QL describes how error messages quote program elements. +** CHANGE it if you want a different appearance. +*/ +#define LUA_QL(x) "'" x "'" +#define LUA_QS LUA_QL("%s") + + +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +@* of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 + + +/* +** {================================================================== +** Stand-alone configuration +** =================================================================== +*/ + +/* +@@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that +@* is, whether we're running lua interactively). +** CHANGE it if you have a better definition for non-POSIX/non-Windows +** systems. +*/ +#if defined(LUA_USE_ISATTY) +#include +#define lua_stdin_is_tty() isatty(0) +#elif defined(LUA_WIN) +#include +#include "c_stdio.h" +#define lua_stdin_is_tty() _isatty(_fileno(stdin)) +#else +#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ +#endif + + +/* +@@ LUA_PROMPT is the default prompt used by stand-alone Lua. +@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua. +** CHANGE them if you want different prompts. (You can also change the +** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.) +*/ +#define LUA_PROMPT "> " +#define LUA_PROMPT2 ">> " + + +/* +@@ LUA_PROGNAME is the default name for the stand-alone Lua program. +** CHANGE it if your stand-alone interpreter has a different name and +** your system is not able to detect that name automatically. +*/ +#define LUA_PROGNAME "lua" + + +/* +@@ LUA_MAXINPUT is the maximum length for an input line in the +@* stand-alone interpreter. +** CHANGE it if you need longer lines. +*/ +#define LUA_MAXINPUT 256 + + +/* +@@ lua_readline defines how to show a prompt and then read a line from +@* the standard input. +@@ lua_saveline defines how to "save" a read line in a "history". +@@ lua_freeline defines how to free a line read by lua_readline. +** CHANGE them if you want to improve this functionality (e.g., by using +** GNU readline and history facilities). +*/ +#if defined(LUA_USE_STDIO) +#if defined(LUA_USE_READLINE) +#include "c_stdio.h" +#include +#include +#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) +#define lua_saveline(L,idx) \ + if (lua_strlen(L,idx) > 0) /* non-empty line? */ \ + add_history(lua_tostring(L, idx)); /* add it to history */ +#define lua_freeline(L,b) ((void)L, c_free(b)) +#else // #if defined(LUA_USE_READLINE) +#define lua_readline(L,b,p) \ + ((void)L, c_fputs(p, c_stdout), c_fflush(c_stdout), /* show prompt */ \ + c_fgets(b, LUA_MAXINPUT, c_stdin) != NULL) /* get line */ +#define lua_saveline(L,idx) { (void)L; (void)idx; } +#define lua_freeline(L,b) { (void)L; (void)b; } +#endif // #if defined(LUA_USE_READLINE) + +#else // #if defined(LUA_USE_STDIO) + +#define lua_readline(L,b,p) (readline4lua(p, b, LUA_MAXINPUT)) +#define lua_saveline(L,idx) { (void)L; (void)idx; } +#define lua_freeline(L,b) { (void)L; (void)b; } + +extern int readline4lua(const char *prompt, char *buffer, int length); + +#endif // #if defined(LUA_USE_STDIO) + +/* +@@ luai_writestring/luai_writeline define how 'print' prints its results. +** They are only used in libraries and the stand-alone program. (The #if +** avoids including 'stdio.h' everywhere.) +*/ +#if !defined(LUA_USE_STDIO) +#define luai_writestring(s, l) c_puts(s) +#define luai_writeline() c_puts("\n") +#endif // defined(LUA_USE_STDIO) + +/* +@@ luai_writestringerror defines how to print error messages. +** (A format string with one argument is enough for Lua...) +*/ +#if !defined(LUA_USE_STDIO) +#define luai_writestringerror(s,p) c_printf((s), (p)) +#endif // defined(LUA_USE_STDIO) + + +/* }================================================================== */ + + +/* +@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles +@* as a percentage. +** CHANGE it if you want the GC to run faster or slower (higher values +** mean larger pauses which mean slower collection.) You can also change +** this value dynamically. +*/ +#define LUAI_GCPAUSE 110 /* 110% (wait memory to grow 10% before next gc) */ + + +/* +@@ LUAI_GCMUL defines the default speed of garbage collection relative to +@* memory allocation as a percentage. +** CHANGE it if you want to change the granularity of the garbage +** collection. (Higher values mean coarser collections. 0 represents +** infinity, where each step performs a full collection.) You can also +** change this value dynamically. +*/ +#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */ + + + +/* +@@ LUA_COMPAT_GETN controls compatibility with old getn behavior. +** CHANGE it (define it) if you want exact compatibility with the +** behavior of setn/getn in Lua 5.0. +*/ +#undef LUA_COMPAT_GETN + +/* +@@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib. +** CHANGE it to undefined as soon as you do not need a global 'loadlib' +** function (the function is still available as 'package.loadlib'). +*/ +#undef LUA_COMPAT_LOADLIB + +/* +@@ LUA_COMPAT_VARARG controls compatibility with old vararg feature. +** CHANGE it to undefined as soon as your programs use only '...' to +** access vararg parameters (instead of the old 'arg' table). +*/ +#define LUA_COMPAT_VARARG + +/* +@@ LUA_COMPAT_MOD controls compatibility with old math.mod function. +** CHANGE it to undefined as soon as your programs use 'math.fmod' or +** the new '%' operator instead of 'math.mod'. +*/ +#define LUA_COMPAT_MOD + +/* +@@ LUA_COMPAT_LSTR controls compatibility with old long string nesting +@* facility. +** CHANGE it to 2 if you want the old behaviour, or undefine it to turn +** off the advisory error when nesting [[...]]. +*/ +#define LUA_COMPAT_LSTR 1 + +/* +@@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name. +** CHANGE it to undefined as soon as you rename 'string.gfind' to +** 'string.gmatch'. +*/ +#define LUA_COMPAT_GFIND + +/* +@@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib' +@* behavior. +** CHANGE it to undefined as soon as you replace to 'luaL_register' +** your uses of 'luaL_openlib' +*/ +#define LUA_COMPAT_OPENLIB + +/* +@@ LUA_STRESS_EMERGENCY_GC enables stress testing code for the Emergency GC. +** CHANGE it to defined if you want to test for Emergency GC related bugs. +** Note that this will make the Lua vm very slow, since it will force a +** full GC on every new allocation. +*/ +#undef LUA_STRESS_EMERGENCY_GC + +/* +@@ luai_apicheck is the assert macro used by the Lua-C API. +** CHANGE luai_apicheck if you want Lua to perform some checks in the +** parameters it gets from API calls. This may slow down the interpreter +** a bit, but may be quite useful when debugging C code that interfaces +** with Lua. A useful redefinition is to use assert.h. +*/ +#if defined(LUA_USE_APICHECK) +#include +#define luai_apicheck(L,o) { (void)L; assert(o); } +#else +#define luai_apicheck(L,o) { (void)L; } +#endif + + +/* +@@ LUAI_BITSINT defines the number of bits in an int. +** CHANGE here if Lua cannot automatically detect the number of bits of +** your machine. Probably you do not need to change this. +*/ +/* avoid overflows in comparison */ +#if INT_MAX-20 < 32760 +#define LUAI_BITSINT 16 +#elif INT_MAX > 2147483640L +/* int has at least 32 bits */ +#define LUAI_BITSINT 32 +#else +#error "you must define LUA_BITSINT with number of bits in an integer" +#endif + + +/* +@@ LUAI_UINT32 is an unsigned integer with at least 32 bits. +@@ LUAI_INT32 is an signed integer with at least 32 bits. +@@ LUAI_UMEM is an unsigned integer big enough to count the total +@* memory used by Lua. +@@ LUAI_MEM is a signed integer big enough to count the total memory +@* used by Lua. +** CHANGE here if for some weird reason the default definitions are not +** good enough for your machine. (The definitions in the 'else' +** part always works, but may waste space on machines with 64-bit +** longs.) Probably you do not need to change this. +*/ +#if LUAI_BITSINT >= 32 +#define LUAI_UINT32 unsigned int +#define LUAI_INT32 int +#define LUAI_MAXINT32 INT_MAX +#define LUAI_UMEM size_t +#define LUAI_MEM ptrdiff_t +#else +/* 16-bit ints */ +#define LUAI_UINT32 unsigned long +#define LUAI_INT32 long +#define LUAI_MAXINT32 LONG_MAX +#define LUAI_UMEM unsigned long +#define LUAI_MEM long +#endif + + +/* +@@ LUAI_MAXCALLS limits the number of nested calls. +** CHANGE it if you need really deep recursive calls. This limit is +** arbitrary; its only purpose is to stop infinite recursion before +** exhausting memory. +*/ +#define LUAI_MAXCALLS 20000 + + +/* +@@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function +@* can use. +** CHANGE it if you need lots of (Lua) stack space for your C +** functions. This limit is arbitrary; its only purpose is to stop C +** functions to consume unlimited stack space. (must be smaller than +** -LUA_REGISTRYINDEX) +*/ +#define LUAI_MAXCSTACK 8000 + + + +/* +** {================================================================== +** CHANGE (to smaller values) the following definitions if your system +** has a small C stack. (Or you may want to change them to larger +** values if your system has a large C stack and these limits are +** too rigid for you.) Some of these constants control the size of +** stack-allocated arrays used by the compiler or the interpreter, while +** others limit the maximum number of recursive calls that the compiler +** or the interpreter can perform. Values too large may cause a C stack +** overflow for some forms of deep constructs. +** =================================================================== +*/ + + +/* +@@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and +@* syntactical nested non-terminals in a program. +*/ +#define LUAI_MAXCCALLS 200 + + +/* +@@ LUAI_MAXVARS is the maximum number of local variables per function +@* (must be smaller than 250). +*/ +#define LUAI_MAXVARS 50 + + +/* +@@ LUAI_MAXUPVALUES is the maximum number of upvalues per function +@* (must be smaller than 250). +*/ +#define LUAI_MAXUPVALUES 60 + + +/* +@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +*/ +#define LUAL_BUFFERSIZE BUFSIZ + +/* }================================================================== */ + + + + +/* +** {================================================================== +@@ LUA_NUMBER is the type of numbers in Lua. +** CHANGE the following definitions only if you want to build Lua +** with a number type different from double. You may also need to +** change lua_number2int & lua_number2integer. +** =================================================================== +*/ + +/* Define LUA_NUMBER_INTEGRAL to produce a system that uses no + floating point operations by changing the type of Lua numbers from + double to long. It implements division and modulus so that + + x == (x / y) * y + x % y. + + The exponentiation function returns zero for negative exponents. + Defining LUA_NUMBER_INTEGRAL also removes the difftime function, + and the math module should not be used. The string.format function + no longer handles the floating point directives %e, %E, %f, %g, and + %G. */ + +#if defined LUA_NUMBER_INTEGRAL +#define LUA_NUMBER LUA_INTEGER +#else +#define LUA_NUMBER_DOUBLE +#define LUA_NUMBER double +#endif + +/* +@@ LUAI_UACNUMBER is the result of an 'usual argument conversion' +@* over a number. +*/ +#define LUAI_UACNUMBER LUA_NUMBER + + +/* +@@ LUA_NUMBER_SCAN is the format for reading numbers. +@@ LUA_NUMBER_FMT is the format for writing numbers. +@@ lua_number2str converts a number to a string. +@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. +@@ lua_str2number converts a string to a number. +*/ +#if defined LUA_NUMBER_INTEGRAL + #if !defined LUA_INTEGRAL_LONGLONG + #define LUA_NUMBER_SCAN "%ld" + #define LUA_NUMBER_FMT "%ld" + #else + #define LUA_NUMBER_SCAN "%lld" + #define LUA_NUMBER_FMT "%lld" + #endif // #if !defined LUA_INTEGRAL_LONGLONG +#else +#define LUA_NUMBER_SCAN "%lf" +#define LUA_NUMBER_FMT "%.14g" +#endif // #if defined LUA_NUMBER_INTEGRAL +#define lua_number2str(s,n) c_sprintf((s), LUA_NUMBER_FMT, (n)) +#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ +#if defined LUA_NUMBER_INTEGRAL + #if !defined LUA_INTEGRAL_LONGLONG + #define lua_str2number(s,p) c_strtol((s), (p), 10) + #else + #define lua_str2number(s,p) c_strtoll((s), (p), 10) + #endif // #if !defined LUA_INTEGRAL_LONGLONG +#else +#define lua_str2number(s,p) c_strtod((s), (p)) +#endif // #if defined LUA_NUMBER_INTEGRAL + +/* +@@ The luai_num* macros define the primitive operations over numbers. +*/ +#if defined(LUA_CORE) +#include "c_math.h" +#define luai_numadd(a,b) ((a)+(b)) +#define luai_numsub(a,b) ((a)-(b)) +#define luai_nummul(a,b) ((a)*(b)) +#if defined LUA_NUMBER_INTEGRAL +#define luai_numdiv(a,b) \ + (-1/2? \ + (a)/(b): \ + ((((a)<0)==((b)<0))||(((a)%(b))==0)? \ + (a)/(b): \ + (a)/(b)-1)) +#define luai_nummod(a,b) \ + (-1/2? \ + (a)%(b): \ + ((((a)<0)==((b)<0))||(((a)%(b))==0)? \ + (a)%(b): \ + (a)%(b)+(b))) +#define luai_lnumdiv(a,b) \ + ((b)==0? \ + (luaG_runerror(L,"divide by zero"),0): \ + luai_numdiv(a,b)) +#define luai_lnummod(a,b) \ + ((b)==0? \ + (luaG_runerror(L,"modulo by zero"),0): \ + luai_nummod(a,b)) +LUA_NUMBER luai_ipow(LUA_NUMBER, LUA_NUMBER); +#define luai_numpow(a,b) (luai_ipow(a,b)) +#else +#define luai_numdiv(a,b) ((a)/(b)) +#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b)) +#define luai_lnumdiv(a,b) (luai_numdiv(a,b)) +#define luai_lnummod(a,b) (luai_nummod(a,b)) +#define luai_numpow(a,b) (pow(a,b)) +#endif +#define luai_numunm(a) (-(a)) +#define luai_numeq(a,b) ((a)==(b)) +#define luai_numlt(a,b) ((a)<(b)) +#define luai_numle(a,b) ((a)<=(b)) +#define luai_numisnan(a) (!luai_numeq((a), (a))) +#endif + + +/* +@@ lua_number2int is a macro to convert lua_Number to int. +@@ lua_number2integer is a macro to convert lua_Number to lua_Integer. +** CHANGE them if you know a faster way to convert a lua_Number to +** int (with any rounding method and without throwing errors) in your +** system. In Pentium machines, a naive typecast from double to int +** in C is extremely slow, so any alternative is worth trying. +*/ + +/* On a Pentium, resort to a trick */ +#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \ + (defined(__i386) || defined (_M_IX86) || defined(__i386__)) + +/* On a Microsoft compiler, use assembler */ +#if defined(_MSC_VER) + +#define lua_number2int(i,d) __asm fld d __asm fistp i +#define lua_number2integer(i,n) lua_number2int(i, n) + +/* the next trick should work on any Pentium, but sometimes clashes + with a DirectX idiosyncrasy */ +#else + +union luai_Cast { double l_d; long l_l; }; +#define lua_number2int(i,d) \ + { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; } +#define lua_number2integer(i,n) lua_number2int(i, n) + +#endif + + +/* this option always works, but may be slow */ +#else +#define lua_number2int(i,d) ((i)=(int)(d)) +#define lua_number2integer(i,d) ((i)=(lua_Integer)(d)) + +#endif + +/* }================================================================== */ + + +/* +@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment. +** CHANGE it if your system requires alignments larger than double. (For +** instance, if your system supports long doubles and they must be +** aligned in 16-byte boundaries, then you should add long double in the +** union.) Probably you do not need to change this. +*/ +#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; } + + +/* +@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling. +** CHANGE them if you prefer to use longjmp/setjmp even with C++ +** or if want/don't to use _longjmp/_setjmp instead of regular +** longjmp/setjmp. By default, Lua handles errors with exceptions when +** compiling as C++ code, with _longjmp/_setjmp when asked to use them, +** and with longjmp/setjmp otherwise. +*/ +#if defined(__cplusplus) +/* C++ exceptions */ +#define LUAI_THROW(L,c) throw(c) +#define LUAI_TRY(L,c,a) try { a } catch(...) \ + { if ((c)->status == 0) (c)->status = -1; } +#define luai_jmpbuf int /* dummy variable */ + +#elif defined(LUA_USE_ULONGJMP) +/* in Unix, try _longjmp/_setjmp (more efficient) */ +#define LUAI_THROW(L,c) _longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#else +/* default handling with long jumps */ +#define LUAI_THROW(L,c) longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#endif + + +/* +@@ LUA_MAXCAPTURES is the maximum number of captures that a pattern +@* can do during pattern-matching. +** CHANGE it if you need more captures. This limit is arbitrary. +*/ +#define LUA_MAXCAPTURES 10 + + +/* +@@ lua_tmpnam is the function that the OS library uses to create a +@* temporary name. +@@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam. +** CHANGE them if you have an alternative to tmpnam (which is considered +** insecure) or if you want the original tmpnam anyway. By default, Lua +** uses tmpnam except when POSIX is available, where it uses mkstemp. +*/ +#if defined(loslib_c) || defined(luaall_c) + +#if defined(LUA_USE_MKSTEMP) +#include +#define LUA_TMPNAMBUFSIZE 32 +#define lua_tmpnam(b,e) { \ + c_strcpy(b, "/tmp/lua_XXXXXX"); \ + e = mkstemp(b); \ + if (e != -1) close(e); \ + e = (e == -1); } + +#else +#define LUA_TMPNAMBUFSIZE L_tmpnam +#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } +#endif + +#endif + + +/* +@@ lua_popen spawns a new process connected to the current one through +@* the file streams. +** CHANGE it if you have a way to implement it in your system. +*/ +#if defined(LUA_USE_POPEN) + +#define lua_popen(L,c,m) ((void)L, c_fflush(NULL), popen(c,m)) +#define lua_pclose(L,file) ((void)L, (pclose(file) != -1)) + +#elif defined(LUA_WIN) + +#define lua_popen(L,c,m) ((void)L, _popen(c,m)) +#define lua_pclose(L,file) ((void)L, (_pclose(file) != -1)) + +#else + +#define lua_popen(L,c,m) ((void)((void)c, m), \ + luaL_error(L, LUA_QL("popen") " not supported"), (int)0) +#define lua_pclose(L,file) ((void)((void)L, file), 0) + +#endif + +/* +@@ LUA_DL_* define which dynamic-library system Lua should use. +** CHANGE here if Lua has problems choosing the appropriate +** dynamic-library system for your platform (either Windows' DLL, Mac's +** dyld, or Unix's dlopen). If your system is some kind of Unix, there +** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for +** it. To use dlopen you also need to adapt the src/Makefile (probably +** adding -ldl to the linker options), so Lua does not select it +** automatically. (When you change the makefile to add -ldl, you must +** also add -DLUA_USE_DLOPEN.) +** If you do not want any kind of dynamic library, undefine all these +** options. +** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD. +*/ +#if defined(LUA_USE_DLOPEN) +#define LUA_DL_DLOPEN +#endif + +#if defined(LUA_WIN) +#define LUA_DL_DLL +#endif + + +/* +@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State +@* (the data goes just *before* the lua_State pointer). +** CHANGE (define) this if you really need that. This value must be +** a multiple of the maximum alignment required for your machine. +*/ +#define LUAI_EXTRASPACE 0 + + +/* +@@ luai_userstate* allow user-specific actions on threads. +** CHANGE them if you defined LUAI_EXTRASPACE and need to do something +** extra when a thread is created/deleted/resumed/yielded. +*/ +#define luai_userstateopen(L) ((void)L) +#define luai_userstateclose(L) ((void)L) +#define luai_userstatethread(L,L1) ((void)L) +#define luai_userstatefree(L) ((void)L) +#define luai_userstateresume(L,n) ((void)L) +#define luai_userstateyield(L,n) ((void)L) + + +/* +@@ LUA_INTFRMLEN is the length modifier for integer conversions +@* in 'string.format'. +@@ LUA_INTFRM_T is the integer type correspoding to the previous length +@* modifier. +** CHANGE them if your system supports long long or does not support long. +*/ + +#if defined(LUA_USELONGLONG) || defined(LUA_INTEGRAL_LONGLONG) + +#define LUA_INTFRMLEN "ll" +#define LUA_INTFRM_T long long + +#else + +#define LUA_INTFRMLEN "l" +#define LUA_INTFRM_T long + +#endif + + + +/* =================================================================== */ + +/* +** Local configuration. You can use this space to add your redefinitions +** without modifying the main part of the file. +*/ + +/* If you define the next macro you'll get the ability to set rotables as + metatables for tables/userdata/types (but the VM might run slower) +*/ +#if (LUA_OPTIMIZE_MEMORY == 2) && !defined(LUA_CROSS_COMPILER) +#define LUA_META_ROTABLES +#endif + +#if LUA_OPTIMIZE_MEMORY == 2 && defined(LUA_USE_POPEN) +#error "Pipes not supported in aggresive optimization mode (LUA_OPTIMIZE_MEMORY=2)" +#endif + +#endif diff --git a/app/lua/lualib.h b/app/lua/lualib.h new file mode 100644 index 00000000..a475d887 --- /dev/null +++ b/app/lua/lualib.h @@ -0,0 +1,53 @@ +/* +** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua standard libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lualib_h +#define lualib_h + +#include "lua.h" + + +/* Key to file-handle type */ +#define LUA_FILEHANDLE "INT" + + +#define LUA_COLIBNAME "coroutine" +LUALIB_API int (luaopen_base) (lua_State *L); + +#define LUA_TABLIBNAME "table" +LUALIB_API int (luaopen_table) (lua_State *L); + +#define LUA_IOLIBNAME "io" +LUALIB_API int (luaopen_io) (lua_State *L); + +// #define LUA_OSLIBNAME "os" +// LUALIB_API int (luaopen_os) (lua_State *L); + +#define LUA_STRLIBNAME "string" +LUALIB_API int (luaopen_string) (lua_State *L); + +// #define LUA_MATHLIBNAME "math" +// LUALIB_API int (luaopen_math) (lua_State *L); + +// #define LUA_DBLIBNAME "debug" +// LUALIB_API int (luaopen_debug) (lua_State *L); + +#define LUA_LOADLIBNAME "package" +LUALIB_API int (luaopen_package) (lua_State *L); + + +/* open all previous libraries */ +LUALIB_API void (luaL_openlibs) (lua_State *L); + + + +#ifndef lua_assert +#define lua_assert(x) ((void)0) +#endif + + +#endif diff --git a/app/lua/lundump.c b/app/lua/lundump.c new file mode 100644 index 00000000..f2062b29 --- /dev/null +++ b/app/lua/lundump.c @@ -0,0 +1,338 @@ +/* +** $Id: lundump.c,v 2.7.1.4 2008/04/04 19:51:41 roberto Exp $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#include "c_string.h" + +#define lundump_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstring.h" +#include "lundump.h" +#include "lzio.h" + +typedef struct { + lua_State* L; + ZIO* Z; + Mbuffer* b; + const char* name; + int swap; + int numsize; + int toflt; + size_t total; +} LoadState; + +#ifdef LUAC_TRUST_BINARIES +#define IF(c,s) +#define error(S,s) +#else +#define IF(c,s) if (c) error(S,s) + +static void ICACHE_FLASH_ATTR error(LoadState* S, const char* why) +{ + luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why); + luaD_throw(S->L,LUA_ERRSYNTAX); +} +#endif + +#define LoadByte(S) (lu_byte)LoadChar(S) +#define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x)) +#define LoadVector(S,b,n,size) LoadMem(S,b,n,size) + +static void ICACHE_FLASH_ATTR LoadBlock(LoadState* S, void* b, size_t size) +{ + size_t r=luaZ_read(S->Z,b,size); + IF (r!=0, "unexpected end"); + S->total+=size; +} + +static void ICACHE_FLASH_ATTR LoadMem (LoadState* S, void* b, int n, size_t size) +{ + LoadBlock(S,b,n*size); + if (S->swap && b) + { + char* p=(char*) b; + char c; + switch (size) + { + case 1: + break; + case 2: + while (n--) + { + c=p[0]; p[0]=p[1]; p[1]=c; + p+=2; + } + break; + case 4: + while (n--) + { + c=p[0]; p[0]=p[3]; p[3]=c; + c=p[1]; p[1]=p[2]; p[2]=c; + p+=4; + } + break; + case 8: + while (n--) + { + c=p[0]; p[0]=p[7]; p[7]=c; + c=p[1]; p[1]=p[6]; p[6]=c; + c=p[2]; p[2]=p[5]; p[5]=c; + c=p[3]; p[3]=p[4]; p[4]=c; + p+=8; + } + break; + default: + IF(1, "bad size"); + break; + } + } +} + +static int ICACHE_FLASH_ATTR LoadChar(LoadState* S) +{ + char x; + LoadVar(S,x); + return x; +} + +static void ICACHE_FLASH_ATTR Align4(LoadState* S) +{ + while(S->total&3) + LoadChar(S); +} + +static int ICACHE_FLASH_ATTR LoadInt(LoadState* S) +{ + int x; + LoadVar(S,x); + IF (x<0, "bad integer"); + return x; +} + +static lua_Number ICACHE_FLASH_ATTR LoadNumber(LoadState* S) +{ + lua_Number x; + if(S->toflt) + { + switch(S->numsize) + { + case 1: { + int8_t y; + LoadVar(S,y); + x = (lua_Number)y; + } break; + case 2: { + int16_t y; + LoadVar(S,y); + x = (lua_Number)y; + } break; + case 4: { + int32_t y; + LoadVar(S,y); + x = (lua_Number)y; + } break; +#if 0 + case 8: { + int64_t y; + LoadVar(S,y); + x = (lua_Number)y; + } break; +#endif + default: lua_assert(0); + } + } + else + { + LoadVar(S,x); /* should probably handle more cases for float here... */ + } + return x; +} + +static TString* ICACHE_FLASH_ATTR LoadString(LoadState* S) +{ + int32_t size; + LoadVar(S,size); + if (size==0) + return NULL; + else + { + char* s; + if (!luaZ_direct_mode(S->Z)) { + s = luaZ_openspace(S->L,S->b,size); + LoadBlock(S,s,size); + return luaS_newlstr(S->L,s,size-1); /* remove trailing zero */ + } else { + s = (char*)luaZ_get_crt_address(S->Z); + LoadBlock(S,NULL,size); + return luaS_newrolstr(S->L,s,size-1); + } + } +} + +static void ICACHE_FLASH_ATTR LoadCode(LoadState* S, Proto* f) +{ + int n=LoadInt(S); + Align4(S); + if (!luaZ_direct_mode(S->Z)) { + f->code=luaM_newvector(S->L,n,Instruction); + LoadVector(S,f->code,n,sizeof(Instruction)); + } else { + f->code=(Instruction*)luaZ_get_crt_address(S->Z); + LoadVector(S,NULL,n,sizeof(Instruction)); + } + f->sizecode=n; +} + +static Proto* LoadFunction(LoadState* S, TString* p); + +static void ICACHE_FLASH_ATTR LoadConstants(LoadState* S, Proto* f) +{ + int i,n; + n=LoadInt(S); + f->k=luaM_newvector(S->L,n,TValue); + f->sizek=n; + for (i=0; ik[i]); + for (i=0; ik[i]; + int t=LoadChar(S); + switch (t) + { + case LUA_TNIL: + setnilvalue(o); + break; + case LUA_TBOOLEAN: + setbvalue(o,LoadChar(S)!=0); + break; + case LUA_TNUMBER: + setnvalue(o,LoadNumber(S)); + break; + case LUA_TSTRING: + setsvalue2n(S->L,o,LoadString(S)); + break; + default: + error(S,"bad constant"); + break; + } + } + n=LoadInt(S); + f->p=luaM_newvector(S->L,n,Proto*); + f->sizep=n; + for (i=0; ip[i]=NULL; + for (i=0; ip[i]=LoadFunction(S,f->source); +} + +static void ICACHE_FLASH_ATTR LoadDebug(LoadState* S, Proto* f) +{ + int i,n; + n=LoadInt(S); + Align4(S); + if (!luaZ_direct_mode(S->Z)) { + f->lineinfo=luaM_newvector(S->L,n,int); + LoadVector(S,f->lineinfo,n,sizeof(int)); + } else { + f->lineinfo=(int*)luaZ_get_crt_address(S->Z); + LoadVector(S,NULL,n,sizeof(int)); + } + f->sizelineinfo=n; + n=LoadInt(S); + f->locvars=luaM_newvector(S->L,n,LocVar); + f->sizelocvars=n; + for (i=0; ilocvars[i].varname=NULL; + for (i=0; ilocvars[i].varname=LoadString(S); + f->locvars[i].startpc=LoadInt(S); + f->locvars[i].endpc=LoadInt(S); + } + n=LoadInt(S); + f->upvalues=luaM_newvector(S->L,n,TString*); + f->sizeupvalues=n; + for (i=0; iupvalues[i]=NULL; + for (i=0; iupvalues[i]=LoadString(S); +} + +static Proto* ICACHE_FLASH_ATTR LoadFunction(LoadState* S, TString* p) +{ + Proto* f; + if (++S->L->nCcalls > LUAI_MAXCCALLS) error(S,"code too deep"); + f=luaF_newproto(S->L); + if (luaZ_direct_mode(S->Z)) proto_readonly(f); + setptvalue2s(S->L,S->L->top,f); incr_top(S->L); + f->source=LoadString(S); if (f->source==NULL) f->source=p; + f->linedefined=LoadInt(S); + f->lastlinedefined=LoadInt(S); + f->nups=LoadByte(S); + f->numparams=LoadByte(S); + f->is_vararg=LoadByte(S); + f->maxstacksize=LoadByte(S); + LoadCode(S,f); + LoadConstants(S,f); + LoadDebug(S,f); + IF (!luaG_checkcode(f), "bad code"); + S->L->top--; + S->L->nCcalls--; + return f; +} + +static void ICACHE_FLASH_ATTR LoadHeader(LoadState* S) +{ + char h[LUAC_HEADERSIZE]; + char s[LUAC_HEADERSIZE]; + int intck = (((lua_Number)0.5)==0); /* 0=float, 1=int */ + luaU_header(h); + LoadBlock(S,s,LUAC_HEADERSIZE); + S->swap=(s[6]!=h[6]); s[6]=h[6]; /* Check if byte-swapping is needed */ + S->numsize=h[10]=s[10]; /* length of lua_Number */ + S->toflt=(s[11]>intck); /* check if conversion from int lua_Number to flt is needed */ + if(S->toflt) s[11]=h[11]; + IF (c_memcmp(h,s,LUAC_HEADERSIZE)!=0, "bad header"); +} + +/* +** load precompiled chunk +*/ +Proto* ICACHE_FLASH_ATTR luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name) +{ + LoadState S; + if (*name=='@' || *name=='=') + S.name=name+1; + else if (*name==LUA_SIGNATURE[0]) + S.name="binary string"; + else + S.name=name; + S.L=L; + S.Z=Z; + S.b=buff; + LoadHeader(&S); + S.total=0; + return LoadFunction(&S,luaS_newliteral(L,"=?")); +} + +/* +* make header +*/ +void ICACHE_FLASH_ATTR luaU_header (char* h) +{ + int x=1; + c_memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1); + h+=sizeof(LUA_SIGNATURE)-1; + *h++=(char)LUAC_VERSION; + *h++=(char)LUAC_FORMAT; + *h++=(char)*(char*)&x; /* endianness */ + *h++=(char)sizeof(int); + *h++=(char)sizeof(int32_t); + *h++=(char)sizeof(Instruction); + *h++=(char)sizeof(lua_Number); + *h++=(char)(((lua_Number)0.5)==0); /* is lua_Number integral? */ +} diff --git a/app/lua/lundump.h b/app/lua/lundump.h new file mode 100644 index 00000000..852bdc62 --- /dev/null +++ b/app/lua/lundump.h @@ -0,0 +1,60 @@ +/* +** $Id: lundump.h,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#ifndef lundump_h +#define lundump_h + +#include "c_stdint.h" + +#include "lobject.h" +#include "lzio.h" + +typedef uint32_t strsize_t; + +/* info about target machine for cross-compilation */ +typedef struct { + int little_endian; + int sizeof_int; + int sizeof_strsize_t; + int sizeof_lua_Number; + int lua_Number_integral; + int is_arm_fpa; +} DumpTargetInfo; + +/* load one chunk; from lundump.c */ +LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name); + +/* make header; from lundump.c */ +LUAI_FUNC void luaU_header (char* h); + +/* dump one chunk to a different target; from ldump.c */ +int luaU_dump_crosscompile (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip, DumpTargetInfo target); + +/* dump one chunk; from ldump.c */ +LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip); + +#ifdef luac_c +/* print one chunk; from print.c */ +LUAI_FUNC void luaU_print (const Proto* f, int full); +#endif + +/* for header of binary files -- this is Lua 5.1 */ +#define LUAC_VERSION 0x51 + +/* for header of binary files -- this is the official format */ +#define LUAC_FORMAT 0 + +/* size of header of binary files */ +#define LUAC_HEADERSIZE 12 + +/* error codes from cross-compiler */ +/* target integer is too small to hold a value */ +#define LUA_ERR_CC_INTOVERFLOW 101 + +/* target lua_Number is integral but a constant is non-integer */ +#define LUA_ERR_CC_NOTINTEGER 102 + +#endif diff --git a/app/lua/lvm.c b/app/lua/lvm.c new file mode 100644 index 00000000..729706c0 --- /dev/null +++ b/app/lua/lvm.c @@ -0,0 +1,825 @@ +/* +** $Id: lvm.c,v 2.63.1.4 2009/07/01 21:10:33 roberto Exp $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + + +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_string.h" + +#define lvm_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" +#include "lrotable.h" + + +/* limit for table tag-method chains (to avoid loops) */ +#define MAXTAGLOOP 100 + +#if defined LUA_NUMBER_INTEGRAL +LUA_NUMBER ICACHE_FLASH_ATTR luai_ipow(LUA_NUMBER a, LUA_NUMBER b) { + if (b < 0) + return 0; + else if (b == 0) + return 1; + else { + LUA_NUMBER c = 1; + for (;;) { + if (b & 1) + c *= a; + b = b >> 1; + if (b == 0) + return c; + a *= a; + } + } +} +#endif + +const TValue *ICACHE_FLASH_ATTR luaV_tonumber (const TValue *obj, TValue *n) { + lua_Number num; + if (ttisnumber(obj)) return obj; + if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) { + setnvalue(n, num); + return n; + } + else + return NULL; +} + + +int ICACHE_FLASH_ATTR luaV_tostring (lua_State *L, StkId obj) { + if (!ttisnumber(obj)) + return 0; + else { + char s[LUAI_MAXNUMBER2STR]; + ptrdiff_t objr = savestack(L, obj); + lua_Number n = nvalue(obj); + lua_number2str(s, n); + setsvalue2s(L, restorestack(L, objr), luaS_new(L, s)); + return 1; + } +} + + +static void ICACHE_FLASH_ATTR traceexec (lua_State *L, const Instruction *pc) { + lu_byte mask = L->hookmask; + const Instruction *oldpc = L->savedpc; + L->savedpc = pc; + if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) { + resethookcount(L); + luaD_callhook(L, LUA_HOOKCOUNT, -1); + } + if (mask & LUA_MASKLINE) { + Proto *p = ci_func(L->ci)->l.p; + int npc = pcRel(pc, p); + int newline = getline(p, npc); + /* call linehook when enter a new function, when jump back (loop), + or when enter a new line */ + if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p))) + luaD_callhook(L, LUA_HOOKLINE, newline); + } +} + + +static void ICACHE_FLASH_ATTR callTMres (lua_State *L, StkId res, const TValue *f, + const TValue *p1, const TValue *p2) { + ptrdiff_t result = savestack(L, res); + setobj2s(L, L->top, f); /* push function */ + setobj2s(L, L->top+1, p1); /* 1st argument */ + setobj2s(L, L->top+2, p2); /* 2nd argument */ + luaD_checkstack(L, 3); + L->top += 3; + luaD_call(L, L->top - 3, 1); + res = restorestack(L, result); + L->top--; + setobjs2s(L, res, L->top); +} + + + +static void ICACHE_FLASH_ATTR callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, const TValue *p3) { + setobj2s(L, L->top, f); /* push function */ + setobj2s(L, L->top+1, p1); /* 1st argument */ + setobj2s(L, L->top+2, p2); /* 2nd argument */ + setobj2s(L, L->top+3, p3); /* 3th argument */ + luaD_checkstack(L, 4); + L->top += 4; + luaD_call(L, L->top - 4, 0); +} + + +void ICACHE_FLASH_ATTR luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { + int loop; + TValue temp; + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; + if (ttistable(t) || ttisrotable(t)) { /* `t' is a table? */ + void *h = ttistable(t) ? hvalue(t) : rvalue(t); + const TValue *res = ttistable(t) ? luaH_get((Table*)h, key) : luaH_get_ro(h, key); /* do a primitive get */ + if (!ttisnil(res) || /* result is no nil? */ + (tm = fasttm(L, ttistable(t) ? ((Table*)h)->metatable : (Table*)luaR_getmeta(h), TM_INDEX)) == NULL) { /* or no TM? */ + setobj2s(L, val, res); + return; + } + /* else will try the tag method */ + } + else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) + luaG_typeerror(L, t, "index"); + if (ttisfunction(tm) || ttislightfunction(tm)) { + callTMres(L, val, tm, t, key); + return; + } + /* else repeat with `tm' */ + setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */ + t = &temp; + } + luaG_runerror(L, "loop in gettable"); +} + + +void ICACHE_FLASH_ATTR luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { + int loop; + TValue temp; + setnilvalue(L->top); + L->top++; + fixedstack(L); + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; + if (ttistable(t) || ttisrotable(t)) { /* `t' is a table? */ + void *h = ttistable(t) ? hvalue(t) : rvalue(t); + TValue *oldval = ttistable(t) ? luaH_set(L, (Table*)h, key) : NULL; /* do a primitive set */ + if ((oldval && !ttisnil(oldval)) || /* result is no nil? */ + (tm = fasttm(L, ttistable(t) ? ((Table*)h)->metatable : (Table*)luaR_getmeta(h), TM_NEWINDEX)) == NULL) { /* or no TM? */ + if(oldval) { + L->top--; + unfixedstack(L); + setobj2t(L, oldval, val); + ((Table *)h)->flags = 0; + luaC_barriert(L, (Table*)h, val); + } + return; + } + /* else will try the tag method */ + } + else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) + luaG_typeerror(L, t, "index"); + if (ttisfunction(tm) || ttislightfunction(tm)) { + L->top--; + unfixedstack(L); + callTM(L, tm, t, key, val); + return; + } + /* else repeat with `tm' */ + setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */ + t = &temp; + setobj2s(L, L->top-1, t); /* need to protect value from EGC. */ + } + luaG_runerror(L, "loop in settable"); +} + + +static int ICACHE_FLASH_ATTR call_binTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ + if (ttisnil(tm)) + tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ + if (ttisnil(tm)) return 0; + callTMres(L, res, tm, p1, p2); + return 1; +} + + +static const TValue *ICACHE_FLASH_ATTR get_compTM (lua_State *L, Table *mt1, Table *mt2, + TMS event) { + const TValue *tm1 = fasttm(L, mt1, event); + const TValue *tm2; + if (tm1 == NULL) return NULL; /* no metamethod */ + if (mt1 == mt2) return tm1; /* same metatables => same metamethods */ + tm2 = fasttm(L, mt2, event); + if (tm2 == NULL) return NULL; /* no metamethod */ + if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */ + return tm1; + return NULL; +} + + +static int ICACHE_FLASH_ATTR call_orderTM (lua_State *L, const TValue *p1, const TValue *p2, + TMS event) { + const TValue *tm1 = luaT_gettmbyobj(L, p1, event); + const TValue *tm2; + if (ttisnil(tm1)) return -1; /* no metamethod? */ + tm2 = luaT_gettmbyobj(L, p2, event); + if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */ + return -1; + callTMres(L, L->top, tm1, p1, p2); + return !l_isfalse(L->top); +} + + +static int ICACHE_FLASH_ATTR l_strcmp (const TString *ls, const TString *rs) { + const char *l = getstr(ls); + size_t ll = ls->tsv.len; + const char *r = getstr(rs); + size_t lr = rs->tsv.len; + for (;;) { + int temp = c_strcoll(l, r); + if (temp != 0) return temp; + else { /* strings are equal up to a `\0' */ + size_t len = c_strlen(l); /* index of first `\0' in both strings */ + if (len == lr) /* r is finished? */ + return (len == ll) ? 0 : 1; + else if (len == ll) /* l is finished? */ + return -1; /* l is smaller than r (because r is not finished) */ + /* both strings longer than `len'; go on comparing (after the `\0') */ + len++; + l += len; ll -= len; r += len; lr -= len; + } + } +} + + +int ICACHE_FLASH_ATTR luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { + int res; + if (ttype(l) != ttype(r)) + return luaG_ordererror(L, l, r); + else if (ttisnumber(l)) + return luai_numlt(nvalue(l), nvalue(r)); + else if (ttisstring(l)) + return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0; + else if ((res = call_orderTM(L, l, r, TM_LT)) != -1) + return res; + return luaG_ordererror(L, l, r); +} + + +static int ICACHE_FLASH_ATTR lessequal (lua_State *L, const TValue *l, const TValue *r) { + int res; + if (ttype(l) != ttype(r)) + return luaG_ordererror(L, l, r); + else if (ttisnumber(l)) + return luai_numle(nvalue(l), nvalue(r)); + else if (ttisstring(l)) + return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0; + else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */ + return res; + else if ((res = call_orderTM(L, r, l, TM_LT)) != -1) /* else try `lt' */ + return !res; + return luaG_ordererror(L, l, r); +} + + +int ICACHE_FLASH_ATTR luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) { + const TValue *tm; + lua_assert(ttype(t1) == ttype(t2)); + switch (ttype(t1)) { + case LUA_TNIL: return 1; + case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2)); + case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ + case LUA_TLIGHTUSERDATA: + case LUA_TROTABLE: + case LUA_TLIGHTFUNCTION: + return pvalue(t1) == pvalue(t2); + case LUA_TUSERDATA: { + if (uvalue(t1) == uvalue(t2)) return 1; + tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, + TM_EQ); + break; /* will try TM */ + } + case LUA_TTABLE: { + if (hvalue(t1) == hvalue(t2)) return 1; + tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + default: return gcvalue(t1) == gcvalue(t2); + } + if (tm == NULL) return 0; /* no TM? */ + callTMres(L, L->top, tm, t1, t2); /* call TM */ + return !l_isfalse(L->top); +} + + +void ICACHE_FLASH_ATTR luaV_concat (lua_State *L, int total, int last) { + lu_mem max_sizet = MAX_SIZET; + if (G(L)->memlimit < max_sizet) max_sizet = G(L)->memlimit; + do { + StkId top = L->base + last + 1; + int n = 2; /* number of elements handled in this pass (at least 2) */ + if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) { + if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT)) { + /* restore 'top' pointer, since stack might have been reallocted */ + top = L->base + last + 1; + luaG_concaterror(L, top-2, top-1); + } + } else if (tsvalue(top-1)->len == 0) /* second op is empty? */ + (void)tostring(L, top - 2); /* result is first op (as string) */ + else { + /* at least two string values; get as many as possible */ + size_t tl = tsvalue(top-1)->len; + char *buffer; + int i; + fixedstack(L); + /* collect total length */ + for (n = 1; n < total && tostring(L, top-n-1); n++) { + size_t l = tsvalue(top-n-1)->len; + if (l >= max_sizet - tl) luaG_runerror(L, "string length overflow"); + tl += l; + } + G(L)->buff.n = tl; + buffer = luaZ_openspace(L, &G(L)->buff, tl); + tl = 0; + for (i=n; i>0; i--) { /* concat all strings */ + size_t l = tsvalue(top-i)->len; + c_memcpy(buffer+tl, svalue(top-i), l); + tl += l; + } + setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); + luaZ_resetbuffer(&G(L)->buff); + unfixedstack(L); + } + total -= n-1; /* got `n' strings to create 1 new */ + last -= n-1; + } while (total > 1); /* repeat until only 1 result left */ +} + + +static void ICACHE_FLASH_ATTR Arith (lua_State *L, StkId ra, const TValue *rb, + const TValue *rc, TMS op) { + TValue tempb, tempc; + const TValue *b, *c; + if ((b = luaV_tonumber(rb, &tempb)) != NULL && + (c = luaV_tonumber(rc, &tempc)) != NULL) { + lua_Number nb = nvalue(b), nc = nvalue(c); + switch (op) { + case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break; + case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break; + case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break; + case TM_DIV: setnvalue(ra, luai_lnumdiv(nb, nc)); break; + case TM_MOD: setnvalue(ra, luai_lnummod(nb, nc)); break; + case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break; + case TM_UNM: setnvalue(ra, luai_numunm(nb)); break; + default: lua_assert(0); break; + } + } + else { + ptrdiff_t br = savestack(L, rb); + ptrdiff_t cr = savestack(L, rc); + if (!call_binTM(L, rb, rc, ra, op)) { + luaG_aritherror(L, restorestack(L, br), restorestack(L, cr)); + } + } +} + + + +/* +** some macros for common tasks in `luaV_execute' +*/ + +#define runtime_check(L, c) { if (!(c)) break; } + +#define RA(i) (base+GETARG_A(i)) +/* to be used after possible stack reallocation */ +#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) +#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) +#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ + ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) +#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ + ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) +#define KBx(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i)) + + +#define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);} + + +#define Protect(x) { L->savedpc = pc; {x;}; base = L->base; } + + +#define arith_op(op,tm) { \ + TValue *rb = RKB(i); \ + TValue *rc = RKC(i); \ + if (ttisnumber(rb) && ttisnumber(rc)) { \ + lua_Number nb = nvalue(rb), nc = nvalue(rc); \ + setnvalue(ra, op(nb, nc)); \ + } \ + else \ + Protect(Arith(L, ra, rb, rc, tm)); \ + } + + + +void ICACHE_FLASH_ATTR luaV_execute (lua_State *L, int nexeccalls) { + LClosure *cl; + StkId base; + TValue *k; + const Instruction *pc; + reentry: /* entry point */ + lua_assert(isLua(L->ci)); + pc = L->savedpc; + cl = &clvalue(L->ci->func)->l; + base = L->base; + k = cl->p->k; + /* main loop of interpreter */ + for (;;) { + const Instruction i = *pc++; + StkId ra; + if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && + (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { + traceexec(L, pc); + if (L->status == LUA_YIELD) { /* did hook yield? */ + L->savedpc = pc - 1; + return; + } + base = L->base; + } + /* warning!! several calls may realloc the stack and invalidate `ra' */ + ra = RA(i); + lua_assert(base == L->base && L->base == L->ci->base); + lua_assert(base <= L->top && L->top <= L->stack + L->stacksize); + lua_assert(L->top == L->ci->top || luaG_checkopenop(i)); + switch (GET_OPCODE(i)) { + case OP_MOVE: { + setobjs2s(L, ra, RB(i)); + continue; + } + case OP_LOADK: { + setobj2s(L, ra, KBx(i)); + continue; + } + case OP_LOADBOOL: { + setbvalue(ra, GETARG_B(i)); + if (GETARG_C(i)) pc++; /* skip next instruction (if C) */ + continue; + } + case OP_LOADNIL: { + TValue *rb = RB(i); + do { + setnilvalue(rb--); + } while (rb >= ra); + continue; + } + case OP_GETUPVAL: { + int b = GETARG_B(i); + setobj2s(L, ra, cl->upvals[b]->v); + continue; + } + case OP_GETGLOBAL: { + TValue g; + TValue *rb = KBx(i); + sethvalue(L, &g, cl->env); + lua_assert(ttisstring(rb)); + Protect(luaV_gettable(L, &g, rb, ra)); + continue; + } + case OP_GETTABLE: { + Protect(luaV_gettable(L, RB(i), RKC(i), ra)); + continue; + } + case OP_SETGLOBAL: { + TValue g; + sethvalue(L, &g, cl->env); + lua_assert(ttisstring(KBx(i))); + Protect(luaV_settable(L, &g, KBx(i), ra)); + continue; + } + case OP_SETUPVAL: { + UpVal *uv = cl->upvals[GETARG_B(i)]; + setobj(L, uv->v, ra); + luaC_barrier(L, uv, ra); + continue; + } + case OP_SETTABLE: { + Protect(luaV_settable(L, ra, RKB(i), RKC(i))); + continue; + } + case OP_NEWTABLE: { + int b = GETARG_B(i); + int c = GETARG_C(i); + Table *h; + Protect(h = luaH_new(L, luaO_fb2int(b), luaO_fb2int(c))); + sethvalue(L, RA(i), h); + Protect(luaC_checkGC(L)); + continue; + } + case OP_SELF: { + StkId rb = RB(i); + setobjs2s(L, ra+1, rb); + Protect(luaV_gettable(L, rb, RKC(i), ra)); + continue; + } + case OP_ADD: { + arith_op(luai_numadd, TM_ADD); + continue; + } + case OP_SUB: { + arith_op(luai_numsub, TM_SUB); + continue; + } + case OP_MUL: { + arith_op(luai_nummul, TM_MUL); + continue; + } + case OP_DIV: { + arith_op(luai_lnumdiv, TM_DIV); + continue; + } + case OP_MOD: { + arith_op(luai_lnummod, TM_MOD); + continue; + } + case OP_POW: { + arith_op(luai_numpow, TM_POW); + continue; + } + case OP_UNM: { + TValue *rb = RB(i); + if (ttisnumber(rb)) { + lua_Number nb = nvalue(rb); + setnvalue(ra, luai_numunm(nb)); + } + else { + Protect(Arith(L, ra, rb, rb, TM_UNM)); + } + continue; + } + case OP_NOT: { + int res = l_isfalse(RB(i)); /* next assignment may change this value */ + setbvalue(ra, res); + continue; + } + case OP_LEN: { + const TValue *rb = RB(i); + switch (ttype(rb)) { + case LUA_TTABLE: + case LUA_TROTABLE: { + setnvalue(ra, ttistable(rb) ? cast_num(luaH_getn(hvalue(rb))) : cast_num(luaH_getn_ro(rvalue(rb)))); + break; + } + case LUA_TSTRING: { + setnvalue(ra, cast_num(tsvalue(rb)->len)); + break; + } + default: { /* try metamethod */ + ptrdiff_t br = savestack(L, rb); + Protect( + if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN)) + luaG_typeerror(L, restorestack(L, br), "get length of"); + ) + } + } + continue; + } + case OP_CONCAT: { + int b = GETARG_B(i); + int c = GETARG_C(i); + Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L)); + setobjs2s(L, RA(i), base+b); + continue; + } + case OP_JMP: { + dojump(L, pc, GETARG_sBx(i)); + continue; + } + case OP_EQ: { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + Protect( + if (equalobj(L, rb, rc) == GETARG_A(i)) + dojump(L, pc, GETARG_sBx(*pc)); + ) + pc++; + continue; + } + case OP_LT: { + Protect( + if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i)) + dojump(L, pc, GETARG_sBx(*pc)); + ) + pc++; + continue; + } + case OP_LE: { + Protect( + if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i)) + dojump(L, pc, GETARG_sBx(*pc)); + ) + pc++; + continue; + } + case OP_TEST: { + if (l_isfalse(ra) != GETARG_C(i)) + dojump(L, pc, GETARG_sBx(*pc)); + pc++; + continue; + } + case OP_TESTSET: { + TValue *rb = RB(i); + if (l_isfalse(rb) != GETARG_C(i)) { + setobjs2s(L, ra, rb); + dojump(L, pc, GETARG_sBx(*pc)); + } + pc++; + continue; + } + case OP_CALL: { + int b = GETARG_B(i); + int nresults = GETARG_C(i) - 1; + if (b != 0) L->top = ra+b; /* else previous instruction set top */ + L->savedpc = pc; + switch (luaD_precall(L, ra, nresults)) { + case PCRLUA: { + nexeccalls++; + goto reentry; /* restart luaV_execute over new Lua function */ + } + case PCRC: { + /* it was a C function (`precall' called it); adjust results */ + if (nresults >= 0) L->top = L->ci->top; + base = L->base; + continue; + } + default: { + return; /* yield */ + } + } + } + case OP_TAILCALL: { + int b = GETARG_B(i); + if (b != 0) L->top = ra+b; /* else previous instruction set top */ + L->savedpc = pc; + lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); + switch (luaD_precall(L, ra, LUA_MULTRET)) { + case PCRLUA: { + /* tail call: put new frame in place of previous one */ + CallInfo *ci = L->ci - 1; /* previous frame */ + int aux; + StkId func = ci->func; + StkId pfunc = (ci+1)->func; /* previous function index */ + if (L->openupval) luaF_close(L, ci->base); + L->base = ci->base = ci->func + ((ci+1)->base - pfunc); + for (aux = 0; pfunc+aux < L->top; aux++) /* move frame down */ + setobjs2s(L, func+aux, pfunc+aux); + ci->top = L->top = func+aux; /* correct top */ + lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize); + ci->savedpc = L->savedpc; + ci->tailcalls++; /* one more call lost */ + L->ci--; /* remove new frame */ + goto reentry; + } + case PCRC: { /* it was a C function (`precall' called it) */ + base = L->base; + continue; + } + default: { + return; /* yield */ + } + } + } + case OP_RETURN: { + int b = GETARG_B(i); + if (b != 0) L->top = ra+b-1; + if (L->openupval) luaF_close(L, base); + L->savedpc = pc; + b = luaD_poscall(L, ra); + if (--nexeccalls == 0) /* was previous function running `here'? */ + return; /* no: return */ + else { /* yes: continue its execution */ + if (b) L->top = L->ci->top; + lua_assert(isLua(L->ci)); + lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL); + goto reentry; + } + } + case OP_FORLOOP: { + lua_Number step = nvalue(ra+2); + lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */ + lua_Number limit = nvalue(ra+1); + if (luai_numlt(0, step) ? luai_numle(idx, limit) + : luai_numle(limit, idx)) { + dojump(L, pc, GETARG_sBx(i)); /* jump back */ + setnvalue(ra, idx); /* update internal index... */ + setnvalue(ra+3, idx); /* ...and external index */ + } + continue; + } + case OP_FORPREP: { + const TValue *init = ra; + const TValue *plimit = ra+1; + const TValue *pstep = ra+2; + L->savedpc = pc; /* next steps may throw errors */ + if (!tonumber(init, ra)) + luaG_runerror(L, LUA_QL("for") " initial value must be a number"); + else if (!tonumber(plimit, ra+1)) + luaG_runerror(L, LUA_QL("for") " limit must be a number"); + else if (!tonumber(pstep, ra+2)) + luaG_runerror(L, LUA_QL("for") " step must be a number"); + setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep))); + dojump(L, pc, GETARG_sBx(i)); + continue; + } + case OP_TFORLOOP: { + StkId cb = ra + 3; /* call base */ + setobjs2s(L, cb+2, ra+2); + setobjs2s(L, cb+1, ra+1); + setobjs2s(L, cb, ra); + L->top = cb+3; /* func. + 2 args (state and index) */ + Protect(luaD_call(L, cb, GETARG_C(i))); + L->top = L->ci->top; + cb = RA(i) + 3; /* previous call may change the stack */ + if (!ttisnil(cb)) { /* continue loop? */ + setobjs2s(L, cb-1, cb); /* save control variable */ + dojump(L, pc, GETARG_sBx(*pc)); /* jump back */ + } + pc++; + continue; + } + case OP_SETLIST: { + int n = GETARG_B(i); + int c = GETARG_C(i); + int last; + Table *h; + fixedstack(L); + if (n == 0) { + n = cast_int(L->top - ra) - 1; + L->top = L->ci->top; + } + if (c == 0) c = cast_int(*pc++); + runtime_check(L, ttistable(ra)); + h = hvalue(ra); + last = ((c-1)*LFIELDS_PER_FLUSH) + n; + if (last > h->sizearray) /* needs more space? */ + luaH_resizearray(L, h, last); /* pre-alloc it at once */ + for (; n > 0; n--) { + TValue *val = ra+n; + setobj2t(L, luaH_setnum(L, h, last--), val); + luaC_barriert(L, h, val); + } + unfixedstack(L); + continue; + } + case OP_CLOSE: { + luaF_close(L, ra); + continue; + } + case OP_CLOSURE: { + Proto *p; + Closure *ncl; + int nup, j; + p = cl->p->p[GETARG_Bx(i)]; + nup = p->nups; + fixedstack(L); + ncl = luaF_newLclosure(L, nup, cl->env); + setclvalue(L, ra, ncl); + ncl->l.p = p; + for (j=0; jl.upvals[j] = cl->upvals[GETARG_B(*pc)]; + else { + lua_assert(GET_OPCODE(*pc) == OP_MOVE); + ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc)); + } + } + unfixedstack(L); + Protect(luaC_checkGC(L)); + continue; + } + case OP_VARARG: { + int b = GETARG_B(i) - 1; + int j; + CallInfo *ci = L->ci; + int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1; + if (b == LUA_MULTRET) { + Protect(luaD_checkstack(L, n)); + ra = RA(i); /* previous call may change the stack */ + b = n; + L->top = ra + n; + } + for (j = 0; j < b; j++) { + if (j < n) { + setobjs2s(L, ra + j, ci->base - n + j); + } + else { + setnilvalue(ra + j); + } + } + continue; + } + } + } +} + diff --git a/app/lua/lvm.h b/app/lua/lvm.h new file mode 100644 index 00000000..bfe4f567 --- /dev/null +++ b/app/lua/lvm.h @@ -0,0 +1,36 @@ +/* +** $Id: lvm.h,v 2.5.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lvm_h +#define lvm_h + + +#include "ldo.h" +#include "lobject.h" +#include "ltm.h" + + +#define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o))) + +#define tonumber(o,n) (ttype(o) == LUA_TNUMBER || \ + (((o) = luaV_tonumber(o,n)) != NULL)) + +#define equalobj(L,o1,o2) \ + (ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2)) + + +LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2); +LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n); +LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj); +LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key, + StkId val); +LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key, + StkId val); +LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls); +LUAI_FUNC void luaV_concat (lua_State *L, int total, int last); + +#endif diff --git a/app/lua/lzio.c b/app/lua/lzio.c new file mode 100644 index 00000000..fc3ce0c1 --- /dev/null +++ b/app/lua/lzio.c @@ -0,0 +1,85 @@ +/* +** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ +** a generic input stream interface +** See Copyright Notice in lua.h +*/ + + +#include "c_string.h" + +#define lzio_c +#define LUA_CORE + +#include "lua.h" + +#include "llimits.h" +#include "lmem.h" +#include "lstate.h" +#include "lzio.h" + + +int ICACHE_FLASH_ATTR luaZ_fill (ZIO *z) { + size_t size; + lua_State *L = z->L; + const char *buff; + lua_unlock(L); + buff = z->reader(L, z->data, &size); + lua_lock(L); + if (buff == NULL || size == 0) return EOZ; + z->n = size - 1; + z->p = buff; + return char2int(*(z->p++)); +} + + +int ICACHE_FLASH_ATTR luaZ_lookahead (ZIO *z) { + if (z->n == 0) { + if (luaZ_fill(z) == EOZ) + return EOZ; + else { + z->n++; /* luaZ_fill removed first byte; put back it */ + z->p--; + } + } + return char2int(*z->p); +} + + +void ICACHE_FLASH_ATTR luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { + z->L = L; + z->reader = reader; + z->data = data; + z->n = z->i = 0; + z->p = NULL; +} + + +/* --------------------------------------------------------------- read --- */ +size_t ICACHE_FLASH_ATTR luaZ_read (ZIO *z, void *b, size_t n) { + while (n) { + size_t m; + if (luaZ_lookahead(z) == EOZ) + return n; /* return number of missing bytes */ + m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ + if (b) + c_memcpy(b, z->p, m); + z->n -= m; + z->i += m; + z->p += m; + if (b) + b = (char *)b + m; + n -= m; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ +char *ICACHE_FLASH_ATTR luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) { + if (n > buff->buffsize) { + if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; + luaZ_resizebuffer(L, buff, n); + } + return buff->buffer; +} + + diff --git a/app/lua/lzio.h b/app/lua/lzio.h new file mode 100644 index 00000000..7a31475e --- /dev/null +++ b/app/lua/lzio.h @@ -0,0 +1,72 @@ +/* +** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $ +** Buffered streams +** See Copyright Notice in lua.h +*/ + + +#ifndef lzio_h +#define lzio_h + +#include "lua.h" + +#include "lmem.h" + + +#define EOZ (-1) /* end of stream */ + +typedef struct Zio ZIO; + +#define char2int(c) cast(int, cast(unsigned char, (c))) + +#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : luaZ_fill(z)) + +typedef struct Mbuffer { + char *buffer; + size_t n; + size_t buffsize; +} Mbuffer; + +#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->n = 0, (buff)->buffsize = 0) + +#define luaZ_buffer(buff) ((buff)->buffer) +#define luaZ_sizebuffer(buff) ((buff)->buffsize) +#define luaZ_bufflen(buff) ((buff)->n) + +#define luaZ_resetbuffer(buff) ((buff)->n = 0) + + +#define luaZ_resizebuffer(L, buff, size) \ + (luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ + (buff)->buffsize = size) + +#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) + +#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) +#define luaZ_get_base_address(zio) ((const char *)((zio)->reader(NULL, (zio)->data, NULL))) +#define luaZ_direct_mode(zio) (luaZ_get_base_address(zio) != NULL) +#define luaZ_get_crt_address(zio) (luaZ_get_base_address(zio) + (zio)->i) + +LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n); +LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, + void *data); +LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n); /* read next n bytes */ +LUAI_FUNC int luaZ_lookahead (ZIO *z); + + + +/* --------- Private Part ------------------ */ + +struct Zio { + size_t n; /* bytes still unread */ + size_t i; /* buffer offset */ + const char *p; /* current position in buffer */ + lua_Reader reader; + void* data; /* additional data */ + lua_State *L; /* Lua state (for reader) */ +}; + + +LUAI_FUNC int luaZ_fill (ZIO *z); + +#endif diff --git a/app/lua/not_ported/loslib.c b/app/lua/not_ported/loslib.c new file mode 100644 index 00000000..3484180a --- /dev/null +++ b/app/lua/not_ported/loslib.c @@ -0,0 +1,247 @@ +/* +** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $ +** Standard Operating System library +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include +#include + +#define loslib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "lrotable.h" + + +static int os_pushresult (lua_State *L, int i, const char *filename) { + int en = errno; /* calls to Lua API may change this value */ + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + lua_pushfstring(L, "%s: %s", filename, strerror(en)); + lua_pushinteger(L, en); + return 3; + } +} + + +static int os_execute (lua_State *L) { + lua_pushinteger(L, system(luaL_optstring(L, 1, NULL))); + return 1; +} + + +static int os_remove (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + return os_pushresult(L, remove(filename) == 0, filename); +} + + +static int os_rename (lua_State *L) { + const char *fromname = luaL_checkstring(L, 1); + const char *toname = luaL_checkstring(L, 2); + return os_pushresult(L, rename(fromname, toname) == 0, fromname); +} + + +static int os_tmpname (lua_State *L) { + char buff[LUA_TMPNAMBUFSIZE]; + int err; + lua_tmpnam(buff, err); + if (err) + return luaL_error(L, "unable to generate a unique filename"); + lua_pushstring(L, buff); + return 1; +} + + +static int os_getenv (lua_State *L) { + lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ + return 1; +} + + +static int os_clock (lua_State *L) { + lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); + return 1; +} + + +/* +** {====================================================== +** Time/Date operations +** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, +** wday=%w+1, yday=%j, isdst=? } +** ======================================================= +*/ + +static void setfield (lua_State *L, const char *key, int value) { + lua_pushinteger(L, value); + lua_setfield(L, -2, key); +} + +static void setboolfield (lua_State *L, const char *key, int value) { + if (value < 0) /* undefined? */ + return; /* does not set field */ + lua_pushboolean(L, value); + lua_setfield(L, -2, key); +} + +static int getboolfield (lua_State *L, const char *key) { + int res; + lua_getfield(L, -1, key); + res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1); + lua_pop(L, 1); + return res; +} + + +static int getfield (lua_State *L, const char *key, int d) { + int res; + lua_getfield(L, -1, key); + if (lua_isnumber(L, -1)) + res = (int)lua_tointeger(L, -1); + else { + if (d < 0) + return luaL_error(L, "field " LUA_QS " missing in date table", key); + res = d; + } + lua_pop(L, 1); + return res; +} + + +static int os_date (lua_State *L) { + const char *s = luaL_optstring(L, 1, "%c"); + time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL)); + struct tm *stm; + if (*s == '!') { /* UTC? */ + stm = gmtime(&t); + s++; /* skip `!' */ + } + else + stm = localtime(&t); + if (stm == NULL) /* invalid date? */ + lua_pushnil(L); + else if (strcmp(s, "*t") == 0) { + lua_createtable(L, 0, 9); /* 9 = number of fields */ + setfield(L, "sec", stm->tm_sec); + setfield(L, "min", stm->tm_min); + setfield(L, "hour", stm->tm_hour); + setfield(L, "day", stm->tm_mday); + setfield(L, "month", stm->tm_mon+1); + setfield(L, "year", stm->tm_year+1900); + setfield(L, "wday", stm->tm_wday+1); + setfield(L, "yday", stm->tm_yday+1); + setboolfield(L, "isdst", stm->tm_isdst); + } + else { + char cc[3]; + luaL_Buffer b; + cc[0] = '%'; cc[2] = '\0'; + luaL_buffinit(L, &b); + for (; *s; s++) { + if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */ + luaL_addchar(&b, *s); + else { + size_t reslen; + char buff[200]; /* should be big enough for any conversion result */ + cc[1] = *(++s); + reslen = strftime(buff, sizeof(buff), cc, stm); + luaL_addlstring(&b, buff, reslen); + } + } + luaL_pushresult(&b); + } + return 1; +} + + +static int os_time (lua_State *L) { + time_t t; + if (lua_isnoneornil(L, 1)) /* called without args? */ + t = time(NULL); /* get current time */ + else { + struct tm ts; + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); /* make sure table is at the top */ + ts.tm_sec = getfield(L, "sec", 0); + ts.tm_min = getfield(L, "min", 0); + ts.tm_hour = getfield(L, "hour", 12); + ts.tm_mday = getfield(L, "day", -1); + ts.tm_mon = getfield(L, "month", -1) - 1; + ts.tm_year = getfield(L, "year", -1) - 1900; + ts.tm_isdst = getboolfield(L, "isdst"); + t = mktime(&ts); + } + if (t == (time_t)(-1)) + lua_pushnil(L); + else + lua_pushnumber(L, (lua_Number)t); + return 1; +} + +#if !defined LUA_NUMBER_INTEGRAL +static int os_difftime (lua_State *L) { + lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), + (time_t)(luaL_optnumber(L, 2, 0)))); + return 1; +} +#endif + +/* }====================================================== */ + + +static int os_setlocale (lua_State *L) { + static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, + LC_NUMERIC, LC_TIME}; + static const char *const catnames[] = {"all", "collate", "ctype", "monetary", + "numeric", "time", NULL}; + const char *l = luaL_optstring(L, 1, NULL); + int op = luaL_checkoption(L, 2, "all", catnames); + lua_pushstring(L, setlocale(cat[op], l)); + return 1; +} + + +static int os_exit (lua_State *L) { + c_exit(luaL_optint(L, 1, EXIT_SUCCESS)); +} + +#define MIN_OPT_LEVEL 1 +#include "lrodefs.h" +const LUA_REG_TYPE syslib[] = { + {LSTRKEY("clock"), LFUNCVAL(os_clock)}, + {LSTRKEY("date"), LFUNCVAL(os_date)}, +#if !defined LUA_NUMBER_INTEGRAL + {LSTRKEY("difftime"), LFUNCVAL(os_difftime)}, +#endif + {LSTRKEY("execute"), LFUNCVAL(os_execute)}, + {LSTRKEY("exit"), LFUNCVAL(os_exit)}, + {LSTRKEY("getenv"), LFUNCVAL(os_getenv)}, + {LSTRKEY("remove"), LFUNCVAL(os_remove)}, + {LSTRKEY("rename"), LFUNCVAL(os_rename)}, + {LSTRKEY("setlocale"), LFUNCVAL(os_setlocale)}, + {LSTRKEY("time"), LFUNCVAL(os_time)}, + {LSTRKEY("tmpname"), LFUNCVAL(os_tmpname)}, + {LNILKEY, LNILVAL} +}; + +/* }====================================================== */ + + + +LUALIB_API int luaopen_os (lua_State *L) { + LREGISTER(L, LUA_OSLIBNAME, syslib); +} diff --git a/app/lua/not_ported/luac.c b/app/lua/not_ported/luac.c new file mode 100644 index 00000000..7063c425 --- /dev/null +++ b/app/lua/not_ported/luac.c @@ -0,0 +1,242 @@ +/* +** $Id: luac.c,v 1.54 2006/06/02 17:37:11 lhf Exp $ +** Lua compiler (saves bytecodes to files; also list bytecodes) +** See Copyright Notice in lua.h +*/ + +#include "c_errno.h" +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_string.h" + +#define luac_c +#define LUA_CORE + +#include "lua.h" +#include "lauxlib.h" + +#include "ldo.h" +#include "lfunc.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstring.h" +#include "lundump.h" + +#define PROGNAME "luac" /* default program name */ +#define OUTPUT PROGNAME ".out" /* default output file */ + +static int listing=0; /* list bytecodes? */ +static int dumping=1; /* dump bytecodes? */ +static int stripping=0; /* strip debug information? */ +static char Output[]={ OUTPUT }; /* default output file name */ +static const char* output=Output; /* actual output file name */ +static const char* progname=PROGNAME; /* actual program name */ +static DumpTargetInfo target; + +static void fatal(const char* message) +{ + fprintf(stderr,"%s: %s\n",progname,message); + exit(EXIT_FAILURE); +} + +static void cannot(const char* what) +{ + fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno)); + exit(EXIT_FAILURE); +} + +static void usage(const char* message) +{ + if (*message=='-') + fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message); + else + fprintf(stderr,"%s: %s\n",progname,message); + fprintf(stderr, + "usage: %s [options] [filenames].\n" + "Available options are:\n" + " - process stdin\n" + " -l list\n" + " -o name output to file " LUA_QL("name") " (default is \"%s\")\n" + " -p parse only\n" + " -s strip debug information\n" + " -v show version information\n" + " -cci bits cross-compile with given integer size\n" + " -ccn type bits cross-compile with given lua_Number type and size\n" + " -cce endian cross-compile with given endianness ('big' or 'little')\n" + " -- stop handling options\n", + progname,Output); + exit(EXIT_FAILURE); +} + +#define IS(s) (strcmp(argv[i],s)==0) + +static int doargs(int argc, char* argv[]) +{ + int i; + int version=0; + if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0]; + for (i=1; itop+(i))->l.p) + +static const Proto* combine(lua_State* L, int n) +{ + if (n==1) + return toproto(L,-1); + else + { + int i,pc; + Proto* f=luaF_newproto(L); + setptvalue2s(L,L->top,f); incr_top(L); + f->source=luaS_newliteral(L,"=(" PROGNAME ")"); + f->maxstacksize=1; + pc=2*n+1; + f->code=luaM_newvector(L,pc,Instruction); + f->sizecode=pc; + f->p=luaM_newvector(L,n,Proto*); + f->sizep=n; + pc=0; + for (i=0; ip[i]=toproto(L,i-n-1); + f->code[pc++]=CREATE_ABx(OP_CLOSURE,0,i); + f->code[pc++]=CREATE_ABC(OP_CALL,0,1,1); + } + f->code[pc++]=CREATE_ABC(OP_RETURN,0,1,0); + return f; + } +} + +static int writer(lua_State* L, const void* p, size_t size, void* u) +{ + UNUSED(L); + return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0); +} + +struct Smain { + int argc; + char** argv; +}; + +static int pmain(lua_State* L) +{ + struct Smain* s = (struct Smain*)lua_touserdata(L, 1); + int argc=s->argc; + char** argv=s->argv; + const Proto* f; + int i; + if (!lua_checkstack(L,argc)) fatal("too many input files"); + for (i=0; i1); + if (dumping) + { + FILE* D= (output==NULL) ? stdout : fopen(output,"wb"); + if (D==NULL) cannot("open"); + lua_lock(L); + int result=luaU_dump_crosscompile(L,f,writer,D,stripping,target); + lua_unlock(L); + if (result==LUA_ERR_CC_INTOVERFLOW) fatal("value too big or small for target integer type"); + if (result==LUA_ERR_CC_NOTINTEGER) fatal("target lua_Number is integral but fractional value found"); + if (ferror(D)) cannot("write"); + if (fclose(D)) cannot("close"); + } + return 0; +} + +int main(int argc, char* argv[]) +{ + lua_State* L; + struct Smain s; + + int test=1; + target.little_endian=*(char*)&test; + target.sizeof_int=sizeof(int); + target.sizeof_strsize_t=sizeof(strsize_t); + target.sizeof_lua_Number=sizeof(lua_Number); + target.lua_Number_integral=(((lua_Number)0.5)==0); + target.is_arm_fpa=0; + + int i=doargs(argc,argv); + argc-=i; argv+=i; + if (argc<=0) usage("no input files given"); + L=lua_open(); + if (L==NULL) fatal("not enough memory for state"); + s.argc=argc; + s.argv=argv; + if (lua_cpcall(L,pmain,&s)!=0) fatal(lua_tostring(L,-1)); + lua_close(L); + return EXIT_SUCCESS; +} diff --git a/app/lua/not_ported/print.c b/app/lua/not_ported/print.c new file mode 100644 index 00000000..dccce975 --- /dev/null +++ b/app/lua/not_ported/print.c @@ -0,0 +1,227 @@ +/* +** $Id: print.c,v 1.55a 2006/05/31 13:30:05 lhf Exp $ +** print bytecodes +** See Copyright Notice in lua.h +*/ + +#include "c_ctype.h" +#include "c_stdio.h" + +#define luac_c +#define LUA_CORE + +#include "ldebug.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lundump.h" + +#define PrintFunction luaU_print + +#define Sizeof(x) ((int)sizeof(x)) +#define VOID(p) ((const void*)(p)) + +static void PrintString(const TString* ts) +{ + const char* s=getstr(ts); + size_t i,n=ts->tsv.len; + putchar('"'); + for (i=0; ik[i]; + switch (ttype(o)) + { + case LUA_TNIL: + printf("nil"); + break; + case LUA_TBOOLEAN: + printf(bvalue(o) ? "true" : "false"); + break; + case LUA_TNUMBER: + printf(LUA_NUMBER_FMT,nvalue(o)); + break; + case LUA_TSTRING: + PrintString(rawtsvalue(o)); + break; + default: /* cannot happen */ + printf("? type=%d",ttype(o)); + break; + } +} + +static void PrintCode(const Proto* f) +{ + const Instruction* code=f->code; + int pc,n=f->sizecode; + for (pc=0; pc0) printf("[%d]\t",line); else printf("[-]\t"); + printf("%-9s\t",luaP_opnames[o]); + switch (getOpMode(o)) + { + case iABC: + printf("%d",a); + if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (-1-INDEXK(b)) : b); + if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (-1-INDEXK(c)) : c); + break; + case iABx: + if (getBMode(o)==OpArgK) printf("%d %d",a,-1-bx); else printf("%d %d",a,bx); + break; + case iAsBx: + if (o==OP_JMP) printf("%d",sbx); else printf("%d %d",a,sbx); + break; + } + switch (o) + { + case OP_LOADK: + printf("\t; "); PrintConstant(f,bx); + break; + case OP_GETUPVAL: + case OP_SETUPVAL: + printf("\t; %s", (f->sizeupvalues>0) ? getstr(f->upvalues[b]) : "-"); + break; + case OP_GETGLOBAL: + case OP_SETGLOBAL: + printf("\t; %s",svalue(&f->k[bx])); + break; + case OP_GETTABLE: + case OP_SELF: + if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); } + break; + case OP_SETTABLE: + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_POW: + case OP_EQ: + case OP_LT: + case OP_LE: + if (ISK(b) || ISK(c)) + { + printf("\t; "); + if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-"); + printf(" "); + if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-"); + } + break; + case OP_JMP: + case OP_FORLOOP: + case OP_FORPREP: + printf("\t; to %d",sbx+pc+2); + break; + case OP_CLOSURE: + printf("\t; %p",VOID(f->p[bx])); + break; + case OP_SETLIST: + if (c==0) printf("\t; %d",(int)code[++pc]); + else printf("\t; %d",c); + break; + default: + break; + } + printf("\n"); + } +} + +#define SS(x) (x==1)?"":"s" +#define S(x) x,SS(x) + +static void PrintHeader(const Proto* f) +{ + const char* s=getstr(f->source); + if (*s=='@' || *s=='=') + s++; + else if (*s==LUA_SIGNATURE[0]) + s="(bstring)"; + else + s="(string)"; + printf("\n%s <%s:%d,%d> (%d instruction%s, %d bytes at %p)\n", + (f->linedefined==0)?"main":"function",s, + f->linedefined,f->lastlinedefined, + S(f->sizecode),f->sizecode*Sizeof(Instruction),VOID(f)); + printf("%d%s param%s, %d slot%s, %d upvalue%s, ", + f->numparams,f->is_vararg?"+":"",SS(f->numparams), + S(f->maxstacksize),S(f->nups)); + printf("%d local%s, %d constant%s, %d function%s\n", + S(f->sizelocvars),S(f->sizek),S(f->sizep)); +} + +static void PrintConstants(const Proto* f) +{ + int i,n=f->sizek; + printf("constants (%d) for %p:\n",n,VOID(f)); + for (i=0; isizelocvars; + printf("locals (%d) for %p:\n",n,VOID(f)); + for (i=0; ilocvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1); + } +} + +static void PrintUpvalues(const Proto* f) +{ + int i,n=f->sizeupvalues; + printf("upvalues (%d) for %p:\n",n,VOID(f)); + if (f->upvalues==NULL) return; + for (i=0; iupvalues[i])); + } +} + +void PrintFunction(const Proto* f, int full) +{ + int i,n=f->sizep; + PrintHeader(f); + PrintCode(f); + if (full) + { + PrintConstants(f); + PrintLocals(f); + PrintUpvalues(f); + } + for (i=0; ip[i],full); +} diff --git a/app/lua/not_used/ldblib.c b/app/lua/not_used/ldblib.c new file mode 100644 index 00000000..e2bad1c1 --- /dev/null +++ b/app/lua/not_used/ldblib.c @@ -0,0 +1,406 @@ +/* +** $Id: ldblib.c,v 1.104.1.4 2009/08/04 18:50:18 roberto Exp $ +** Interface from Lua to its debug API +** See Copyright Notice in lua.h +*/ + + +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_string.h" + +#define ldblib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "lrotable.h" + + + +static int ICACHE_FLASH_ATTR db_getregistry (lua_State *L) { + lua_pushvalue(L, LUA_REGISTRYINDEX); + return 1; +} + + +static int ICACHE_FLASH_ATTR db_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); /* no metatable */ + } + return 1; +} + + +static int ICACHE_FLASH_ATTR db_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, + "nil or table expected"); + lua_settop(L, 2); + lua_pushboolean(L, lua_setmetatable(L, 1)); + return 1; +} + + +static int ICACHE_FLASH_ATTR db_getfenv (lua_State *L) { + luaL_checkany(L, 1); + lua_getfenv(L, 1); + return 1; +} + + +static int ICACHE_FLASH_ATTR db_setfenv (lua_State *L) { + luaL_checktype(L, 2, LUA_TTABLE); + lua_settop(L, 2); + if (lua_setfenv(L, 1) == 0) + luaL_error(L, LUA_QL("setfenv") + " cannot change environment of given object"); + return 1; +} + + +static void ICACHE_FLASH_ATTR settabss (lua_State *L, const char *i, const char *v) { + lua_pushstring(L, v); + lua_setfield(L, -2, i); +} + + +static void ICACHE_FLASH_ATTR settabsi (lua_State *L, const char *i, int v) { + lua_pushinteger(L, v); + lua_setfield(L, -2, i); +} + + +static lua_State *ICACHE_FLASH_ATTR getthread (lua_State *L, int *arg) { + if (lua_isthread(L, 1)) { + *arg = 1; + return lua_tothread(L, 1); + } + else { + *arg = 0; + return L; + } +} + + +static void ICACHE_FLASH_ATTR treatstackoption (lua_State *L, lua_State *L1, const char *fname) { + if (L == L1) { + lua_pushvalue(L, -2); + lua_remove(L, -3); + } + else + lua_xmove(L1, L, 1); + lua_setfield(L, -2, fname); +} + + +static int ICACHE_FLASH_ATTR db_getinfo (lua_State *L) { + lua_Debug ar; + int arg; + lua_State *L1 = getthread(L, &arg); + const char *options = luaL_optstring(L, arg+2, "flnSu"); + if (lua_isnumber(L, arg+1)) { + if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) { + lua_pushnil(L); /* level out of range */ + return 1; + } + } + else if (lua_isfunction(L, arg+1) || lua_islightfunction(L, arg+1)) { + lua_pushfstring(L, ">%s", options); + options = lua_tostring(L, -1); + lua_pushvalue(L, arg+1); + lua_xmove(L, L1, 1); + } + else + return luaL_argerror(L, arg+1, "function or level expected"); + if (!lua_getinfo(L1, options, &ar)) + return luaL_argerror(L, arg+2, "invalid option"); + lua_createtable(L, 0, 2); + if (c_strchr(options, 'S')) { + settabss(L, "source", ar.source); + settabss(L, "short_src", ar.short_src); + settabsi(L, "linedefined", ar.linedefined); + settabsi(L, "lastlinedefined", ar.lastlinedefined); + settabss(L, "what", ar.what); + } + if (c_strchr(options, 'l')) + settabsi(L, "currentline", ar.currentline); + if (c_strchr(options, 'u')) + settabsi(L, "nups", ar.nups); + if (c_strchr(options, 'n')) { + settabss(L, "name", ar.name); + settabss(L, "namewhat", ar.namewhat); + } + if (c_strchr(options, 'L')) + treatstackoption(L, L1, "activelines"); + if (c_strchr(options, 'f')) + treatstackoption(L, L1, "func"); + return 1; /* return table */ +} + + +static int ICACHE_FLASH_ATTR db_getlocal (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + const char *name; + if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2)); + if (name) { + lua_xmove(L1, L, 1); + lua_pushstring(L, name); + lua_pushvalue(L, -2); + return 2; + } + else { + lua_pushnil(L); + return 1; + } +} + + +static int ICACHE_FLASH_ATTR db_setlocal (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + luaL_checkany(L, arg+3); + lua_settop(L, arg+3); + lua_xmove(L, L1, 1); + lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2))); + return 1; +} + + +static int ICACHE_FLASH_ATTR auxupvalue (lua_State *L, int get) { + const char *name; + int n = luaL_checkint(L, 2); + luaL_checktype(L, 1, LUA_TFUNCTION); + if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ + name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); + if (name == NULL) return 0; + lua_pushstring(L, name); + lua_insert(L, -(get+1)); + return get + 1; +} + + +static int ICACHE_FLASH_ATTR db_getupvalue (lua_State *L) { + return auxupvalue(L, 1); +} + + +static int ICACHE_FLASH_ATTR db_setupvalue (lua_State *L) { + luaL_checkany(L, 3); + return auxupvalue(L, 0); +} + + + +static const char KEY_HOOK = 'h'; + + +static void ICACHE_FLASH_ATTR hookf (lua_State *L, lua_Debug *ar) { + static const char *const hooknames[] = + {"call", "return", "line", "count", "tail return"}; + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(L, L); + lua_rawget(L, -2); + if (lua_isfunction(L, -1)) { + lua_pushstring(L, hooknames[(int)ar->event]); + if (ar->currentline >= 0) + lua_pushinteger(L, ar->currentline); + else lua_pushnil(L); + lua_assert(lua_getinfo(L, "lS", ar)); + lua_call(L, 2, 0); + } +} + + +static int ICACHE_FLASH_ATTR makemask (const char *smask, int count) { + int mask = 0; + if (c_strchr(smask, 'c')) mask |= LUA_MASKCALL; + if (c_strchr(smask, 'r')) mask |= LUA_MASKRET; + if (c_strchr(smask, 'l')) mask |= LUA_MASKLINE; + if (count > 0) mask |= LUA_MASKCOUNT; + return mask; +} + + +static char *ICACHE_FLASH_ATTR unmakemask (int mask, char *smask) { + int i = 0; + if (mask & LUA_MASKCALL) smask[i++] = 'c'; + if (mask & LUA_MASKRET) smask[i++] = 'r'; + if (mask & LUA_MASKLINE) smask[i++] = 'l'; + smask[i] = '\0'; + return smask; +} + + +static void ICACHE_FLASH_ATTR gethooktable (lua_State *L) { + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + lua_createtable(L, 0, 1); + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + } +} + + +static int ICACHE_FLASH_ATTR db_sethook (lua_State *L) { + int arg, mask, count; + lua_Hook func; + lua_State *L1 = getthread(L, &arg); + if (lua_isnoneornil(L, arg+1)) { + lua_settop(L, arg+1); + func = NULL; mask = 0; count = 0; /* turn off hooks */ + } + else { + const char *smask = luaL_checkstring(L, arg+2); + luaL_checkanyfunction(L, arg+1); + count = luaL_optint(L, arg+3, 0); + func = hookf; mask = makemask(smask, count); + } + gethooktable(L); + lua_pushlightuserdata(L, L1); + lua_pushvalue(L, arg+1); + lua_rawset(L, -3); /* set new hook */ + lua_pop(L, 1); /* remove hook table */ + lua_sethook(L1, func, mask, count); /* set hooks */ + return 0; +} + + +static int ICACHE_FLASH_ATTR db_gethook (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + char buff[5]; + int mask = lua_gethookmask(L1); + lua_Hook hook = lua_gethook(L1); + if (hook != NULL && hook != hookf) /* external hook? */ + lua_pushliteral(L, "external hook"); + else { + gethooktable(L); + lua_pushlightuserdata(L, L1); + lua_rawget(L, -2); /* get hook */ + lua_remove(L, -2); /* remove hook table */ + } + lua_pushstring(L, unmakemask(mask, buff)); + lua_pushinteger(L, lua_gethookcount(L1)); + return 3; +} + + +static int ICACHE_FLASH_ATTR db_debug (lua_State *L) { + for (;;) { + char buffer[LUA_MAXINPUT]; +#if defined(LUA_USE_STDIO) + c_fputs("lua_debug> ", c_stderr); + if (c_fgets(buffer, sizeof(buffer), c_stdin) == 0 || +#else +// luai_writestringerror("%s", "lua_debug>"); + if (lua_readline(L, buffer, "lua_debug>") == 0 || +#endif + c_strcmp(buffer, "cont\n") == 0) + return 0; + if (luaL_loadbuffer(L, buffer, c_strlen(buffer), "=(debug command)") || + lua_pcall(L, 0, 0, 0)) { +#if defined(LUA_USE_STDIO) + c_fputs(lua_tostring(L, -1), c_stderr); + c_fputs("\n", c_stderr); +#else + luai_writestringerror("%s\n", lua_tostring(L, -1)); +#endif + } + lua_settop(L, 0); /* remove eventual returns */ + } +} + + +#define LEVELS1 12 /* size of the first part of the stack */ +#define LEVELS2 10 /* size of the second part of the stack */ + +static int ICACHE_FLASH_ATTR db_errorfb (lua_State *L) { + int level; + int firstpart = 1; /* still before eventual `...' */ + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + if (lua_isnumber(L, arg+2)) { + level = (int)lua_tointeger(L, arg+2); + lua_pop(L, 1); + } + else + level = (L == L1) ? 1 : 0; /* level 0 may be this own function */ + if (lua_gettop(L) == arg) + lua_pushliteral(L, ""); + else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */ + else lua_pushliteral(L, "\n"); + lua_pushliteral(L, "stack traceback:"); + while (lua_getstack(L1, level++, &ar)) { + if (level > LEVELS1 && firstpart) { + /* no more than `LEVELS2' more levels? */ + if (!lua_getstack(L1, level+LEVELS2, &ar)) + level--; /* keep going */ + else { + lua_pushliteral(L, "\n\t..."); /* too many levels */ + while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */ + level++; + } + firstpart = 0; + continue; + } + lua_pushliteral(L, "\n\t"); + lua_getinfo(L1, "Snl", &ar); + lua_pushfstring(L, "%s:", ar.short_src); + if (ar.currentline > 0) + lua_pushfstring(L, "%d:", ar.currentline); + if (*ar.namewhat != '\0') /* is there a name? */ + lua_pushfstring(L, " in function " LUA_QS, ar.name); + else { + if (*ar.what == 'm') /* main? */ + lua_pushfstring(L, " in main chunk"); + else if (*ar.what == 'C' || *ar.what == 't') + lua_pushliteral(L, " ?"); /* C function or tail call */ + else + lua_pushfstring(L, " in function <%s:%d>", + ar.short_src, ar.linedefined); + } + lua_concat(L, lua_gettop(L) - arg); + } + lua_concat(L, lua_gettop(L) - arg); + return 1; +} + +#define MIN_OPT_LEVEL 1 +#include "lrodefs.h" +const LUA_REG_TYPE dblib[] = { + {LSTRKEY("debug"), LFUNCVAL(db_debug)}, + {LSTRKEY("getfenv"), LFUNCVAL(db_getfenv)}, + {LSTRKEY("gethook"), LFUNCVAL(db_gethook)}, + {LSTRKEY("getinfo"), LFUNCVAL(db_getinfo)}, + {LSTRKEY("getlocal"), LFUNCVAL(db_getlocal)}, + {LSTRKEY("getregistry"), LFUNCVAL(db_getregistry)}, + {LSTRKEY("getmetatable"), LFUNCVAL(db_getmetatable)}, + {LSTRKEY("getupvalue"), LFUNCVAL(db_getupvalue)}, + {LSTRKEY("setfenv"), LFUNCVAL(db_setfenv)}, + {LSTRKEY("sethook"), LFUNCVAL(db_sethook)}, + {LSTRKEY("setlocal"), LFUNCVAL(db_setlocal)}, + {LSTRKEY("setmetatable"), LFUNCVAL(db_setmetatable)}, + {LSTRKEY("setupvalue"), LFUNCVAL(db_setupvalue)}, + {LSTRKEY("traceback"), LFUNCVAL(db_errorfb)}, + {LNILKEY, LNILVAL} +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_debug (lua_State *L) { + LREGISTER(L, LUA_DBLIBNAME, dblib); +} diff --git a/app/lwip/Makefile b/app/lwip/Makefile new file mode 100644 index 00000000..37d6e747 --- /dev/null +++ b/app/lwip/Makefile @@ -0,0 +1,50 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +UP_EXTRACT_DIR = .. +GEN_LIBS = liblwip.a +COMPONENTS_liblwip = api/liblwipapi.a \ + app/liblwipapp.a \ + core/liblwipcore.a \ + core/ipv4/liblwipipv4.a \ + netif/liblwipnetif.a +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/lwip/api/Makefile b/app/lwip/api/Makefile new file mode 100644 index 00000000..69b027d2 --- /dev/null +++ b/app/lwip/api/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = liblwipapi.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/lwip/api/api_lib.c b/app/lwip/api/api_lib.c new file mode 100644 index 00000000..158325b0 --- /dev/null +++ b/app/lwip/api/api_lib.c @@ -0,0 +1,740 @@ +/** + * @file + * Sequential API External module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* This is the part of the API that is linked with + the application */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api.h" +#include "lwip/tcpip.h" +#include "lwip/memp.h" + +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" + +#include + +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is also created. + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback) +{ + struct netconn *conn; + struct api_msg msg; + + conn = netconn_alloc(t, callback); + if (conn != NULL) { + msg.function = do_newconn; + msg.msg.msg.n.proto = proto; + msg.msg.conn = conn; + if (TCPIP_APIMSG(&msg) != ERR_OK) { + LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); + LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed)); + LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox)); +#if LWIP_TCP + LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox)); +#endif /* LWIP_TCP */ + sys_sem_free(&conn->op_completed); + sys_mbox_free(&conn->recvmbox); + memp_free(MEMP_NETCONN, conn); + return NULL; + } + } + return conn; +} + +/** + * Close a netconn 'connection' and free its resources. + * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate + * after this returns. + * + * @param conn the netconn to delete + * @return ERR_OK if the connection was deleted + */ +err_t +netconn_delete(struct netconn *conn) +{ + struct api_msg msg; + + /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */ + if (conn == NULL) { + return ERR_OK; + } + + msg.function = do_delconn; + msg.msg.conn = conn; + tcpip_apimsg(&msg); + + netconn_free(conn); + + /* don't care for return value of do_delconn since it only calls void functions */ + + return ERR_OK; +} + +/** + * Get the local or remote IP address and port of a netconn. + * For RAW netconns, this returns the protocol instead of a port! + * + * @param conn the netconn to query + * @param addr a pointer to which to save the IP address + * @param port a pointer to which to save the port (or protocol for RAW) + * @param local 1 to get the local IP address, 0 to get the remote one + * @return ERR_CONN for invalid connections + * ERR_OK if the information was retrieved + */ +err_t +netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;); + + msg.function = do_getaddr; + msg.msg.conn = conn; + msg.msg.msg.ad.ipaddr = addr; + msg.msg.msg.ad.port = port; + msg.msg.msg.ad.local = local; + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Bind a netconn to a specific local IP address and port. + * Binding one netconn twice might not always be checked correctly! + * + * @param conn the netconn to bind + * @param addr the local IP address to bind the netconn to (use IP_ADDR_ANY + * to bind to all addresses) + * @param port the local port to bind the netconn to (not used for RAW) + * @return ERR_OK if bound, any other err_t on failure + */ +err_t +netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_bind; + msg.msg.conn = conn; + msg.msg.msg.bc.ipaddr = addr; + msg.msg.msg.bc.port = port; + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Connect a netconn to a specific remote IP address and port. + * + * @param conn the netconn to connect + * @param addr the remote IP address to connect to + * @param port the remote port to connect to (no used for RAW) + * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise + */ +err_t +netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_connect; + msg.msg.conn = conn; + msg.msg.msg.bc.ipaddr = addr; + msg.msg.msg.bc.port = port; + /* This is the only function which need to not block tcpip_thread */ + err = tcpip_apimsg(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Disconnect a netconn from its current peer (only valid for UDP netconns). + * + * @param conn the netconn to disconnect + * @return TODO: return value is not set here... + */ +err_t +netconn_disconnect(struct netconn *conn) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_disconnect; + msg.msg.conn = conn; + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Set a TCP netconn into listen mode + * + * @param conn the tcp netconn to set to listen mode + * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1 + * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns + * don't return any error (yet?)) + */ +err_t +netconn_listen_with_backlog(struct netconn *conn, u8_t backlog) +{ +#if LWIP_TCP + struct api_msg msg; + err_t err; + + /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */ + LWIP_UNUSED_ARG(backlog); + + LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_listen; + msg.msg.conn = conn; +#if TCP_LISTEN_BACKLOG + msg.msg.msg.lb.backlog = backlog; +#endif /* TCP_LISTEN_BACKLOG */ + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(backlog); + return ERR_ARG; +#endif /* LWIP_TCP */ +} + +/** + * Accept a new connection on a TCP listening netconn. + * + * @param conn the TCP listen netconn + * @param new_conn pointer where the new connection is stored + * @return ERR_OK if a new connection has been received or an error + * code otherwise + */ +err_t +netconn_accept(struct netconn *conn, struct netconn **new_conn) +{ +#if LWIP_TCP + struct netconn *newconn; + err_t err; +#if TCP_LISTEN_BACKLOG + struct api_msg msg; +#endif /* TCP_LISTEN_BACKLOG */ + + LWIP_ERROR("netconn_accept: invalid pointer", (new_conn != NULL), return ERR_ARG;); + *new_conn = NULL; + LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_accept: invalid acceptmbox", sys_mbox_valid(&conn->acceptmbox), return ERR_ARG;); + + err = conn->last_err; + if (ERR_IS_FATAL(err)) { + /* don't recv on fatal errors: this might block the application task + waiting on acceptmbox forever! */ + return err; + } + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { + NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT); + return ERR_TIMEOUT; + } +#else + sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); + + if (newconn == NULL) { + /* connection has been closed */ + NETCONN_SET_SAFE_ERR(conn, ERR_CLSD); + return ERR_CLSD; + } +#if TCP_LISTEN_BACKLOG + /* Let the stack know that we have accepted the connection. */ + msg.function = do_recv; + msg.msg.conn = conn; + /* don't care for the return value of do_recv */ + TCPIP_APIMSG(&msg); +#endif /* TCP_LISTEN_BACKLOG */ + + *new_conn = newconn; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(new_conn); + return ERR_ARG; +#endif /* LWIP_TCP */ +} + +/** + * Receive data: actual implementation that doesn't care whether pbuf or netbuf + * is received + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new pbuf/netbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + */ +static err_t +netconn_recv_data(struct netconn *conn, void **new_buf) +{ + void *buf = NULL; + u16_t len; + err_t err; +#if LWIP_TCP + struct api_msg msg; +#endif /* LWIP_TCP */ + + LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;); + *new_buf = NULL; + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;); + + err = conn->last_err; + if (ERR_IS_FATAL(err)) { + /* don't recv on fatal errors: this might block the application task + waiting on recvmbox forever! */ + /* @todo: this does not allow us to fetch data that has been put into recvmbox + before the fatal error occurred - is that a problem? */ + return err; + } + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { + NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT); + return ERR_TIMEOUT; + } +#else + sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + +#if LWIP_TCP + if (conn->type == NETCONN_TCP) { + if (!netconn_get_noautorecved(conn) || (buf == NULL)) { + /* Let the stack know that we have taken the data. */ + /* TODO: Speedup: Don't block and wait for the answer here + (to prevent multiple thread-switches). */ + msg.function = do_recv; + msg.msg.conn = conn; + if (buf != NULL) { + msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len; + } else { + msg.msg.msg.r.len = 1; + } + /* don't care for the return value of do_recv */ + TCPIP_APIMSG(&msg); + } + + /* If we are closed, we indicate that we no longer wish to use the socket */ + if (buf == NULL) { + API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); + /* Avoid to lose any previous error code */ + NETCONN_SET_SAFE_ERR(conn, ERR_CLSD); + return ERR_CLSD; + } + len = ((struct pbuf *)buf)->tot_len; + } +#endif /* LWIP_TCP */ +#if LWIP_TCP && (LWIP_UDP || LWIP_RAW) + else +#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ +#if (LWIP_UDP || LWIP_RAW) + { + LWIP_ASSERT("buf != NULL", buf != NULL); + len = netbuf_len((struct netbuf *)buf); + } +#endif /* (LWIP_UDP || LWIP_RAW) */ + +#if LWIP_SO_RCVBUF + SYS_ARCH_DEC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, len); + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len)); + + *new_buf = buf; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; +} + +/** + * Receive data (in form of a pbuf) from a TCP netconn + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new pbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + * ERR_ARG if conn is not a TCP netconn + */ +err_t +netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf) +{ + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) && + netconn_type(conn) == NETCONN_TCP, return ERR_ARG;); + + return netconn_recv_data(conn, (void **)new_buf); +} + +/** + * Receive data (in form of a netbuf containing a packet buffer) from a netconn + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new netbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + */ +err_t +netconn_recv(struct netconn *conn, struct netbuf **new_buf) +{ +#if LWIP_TCP + struct netbuf *buf = NULL; + err_t err; +#endif /* LWIP_TCP */ + + LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;); + *new_buf = NULL; + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;); + +#if LWIP_TCP + if (conn->type == NETCONN_TCP) { + struct pbuf *p = NULL; + /* This is not a listening netconn, since recvmbox is set */ + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + NETCONN_SET_SAFE_ERR(conn, ERR_MEM); + return ERR_MEM; + } + + err = netconn_recv_data(conn, (void **)&p); + if (err != ERR_OK) { + memp_free(MEMP_NETBUF, buf); + return err; + } + LWIP_ASSERT("p != NULL", p != NULL); + + buf->p = p; + buf->ptr = p; + buf->port = 0; + ip_addr_set_any(&buf->addr); + *new_buf = buf; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; + } else +#endif /* LWIP_TCP */ + { +#if (LWIP_UDP || LWIP_RAW) + return netconn_recv_data(conn, (void **)new_buf); +#endif /* (LWIP_UDP || LWIP_RAW) */ + } +} + +/** + * TCP: update the receive window: by calling this, the application + * tells the stack that it has processed data and is able to accept + * new data. + * ATTENTION: use with care, this is mainly used for sockets! + * Can only be used when calling netconn_set_noautorecved(conn, 1) before. + * + * @param conn the netconn for which to update the receive window + * @param length amount of data processed (ATTENTION: this must be accurate!) + */ +void +netconn_recved(struct netconn *conn, u32_t length) +{ +#if LWIP_TCP + if ((conn != NULL) && (conn->type == NETCONN_TCP) && + (netconn_get_noautorecved(conn))) { + struct api_msg msg; + /* Let the stack know that we have taken the data. */ + /* TODO: Speedup: Don't block and wait for the answer here + (to prevent multiple thread-switches). */ + msg.function = do_recv; + msg.msg.conn = conn; + msg.msg.msg.r.len = length; + /* don't care for the return value of do_recv */ + TCPIP_APIMSG(&msg); + } +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(length); +#endif /* LWIP_TCP */ +} + +/** + * Send data (in form of a netbuf) to a specific remote IP address and port. + * Only to be used for UDP and RAW netconns (not TCP). + * + * @param conn the netconn over which to send data + * @param buf a netbuf containing the data to send + * @param addr the remote IP address to which to send the data + * @param port the remote port to which to send the data + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port) +{ + if (buf != NULL) { + ip_addr_set(&buf->addr, addr); + buf->port = port; + return netconn_send(conn, buf); + } + return ERR_VAL; +} + +/** + * Send data over a UDP or RAW netconn (that is already connected). + * + * @param conn the UDP or RAW netconn over which to send data + * @param buf a netbuf containing the data to send + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_send(struct netconn *conn, struct netbuf *buf) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;); + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len)); + msg.function = do_send; + msg.msg.conn = conn; + msg.msg.msg.b = buf; + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Send data over a TCP netconn. + * + * @param conn the TCP netconn over which to send data + * @param dataptr pointer to the application buffer that contains the data to send + * @param size size of the application data to send + * @param apiflags combination of following flags : + * - NETCONN_COPY: data will be copied into memory belonging to the stack + * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent + * - NETCONN_DONTBLOCK: only write the data if all dat can be written at once + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apiflags) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_write: invalid conn->type", (conn->type == NETCONN_TCP), return ERR_VAL;); + if (size == 0) { + return ERR_OK; + } + + /* @todo: for non-blocking write, check if 'size' would ever fit into + snd_queue or snd_buf */ + msg.function = do_write; + msg.msg.conn = conn; + msg.msg.msg.w.dataptr = dataptr; + msg.msg.msg.w.apiflags = apiflags; + msg.msg.msg.w.len = size; + /* For locking the core: this _can_ be delayed on low memory/low send buffer, + but if it is, this is done inside api_msg.c:do_write(), so we can use the + non-blocking version here. */ + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Close ot shutdown a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close or shutdown + * @param how fully close or only shutdown one side? + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +static err_t +netconn_close_shutdown(struct netconn *conn, u8_t how) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_close; + msg.msg.conn = conn; + /* shutting down both ends is the same as closing */ + msg.msg.msg.sd.shut = how; + /* because of the LWIP_TCPIP_CORE_LOCKING implementation of do_close, + don't use TCPIP_APIMSG here */ + err = tcpip_apimsg(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Close a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_close(struct netconn *conn) +{ + /* shutting down both ends is the same as closing */ + return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR); +} + +/** + * Shut down one or both sides of a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to shut down + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx) +{ + return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0)); +} + +#if LWIP_IGMP +/** + * Join multicast groups for UDP netconns. + * + * @param conn the UDP netconn for which to change multicast addresses + * @param multiaddr IP address of the multicast group to join or leave + * @param netif_addr the IP address of the network interface on which to send + * the igmp message + * @param join_or_leave flag whether to send a join- or leave-message + * @return ERR_OK if the action was taken, any err_t on error + */ +err_t +netconn_join_leave_group(struct netconn *conn, + ip_addr_t *multiaddr, + ip_addr_t *netif_addr, + enum netconn_igmp join_or_leave) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_join_leave_group; + msg.msg.conn = conn; + msg.msg.msg.jl.multiaddr = multiaddr; + msg.msg.msg.jl.netif_addr = netif_addr; + msg.msg.msg.jl.join_or_leave = join_or_leave; + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Execute a DNS query, only one IP address is returned + * + * @param name a string representation of the DNS host name to query + * @param addr a preallocated ip_addr_t where to store the resolved IP address + * @return ERR_OK: resolving succeeded + * ERR_MEM: memory error, try again later + * ERR_ARG: dns client not initialized or invalid hostname + * ERR_VAL: dns server response was invalid + */ +err_t +netconn_gethostbyname(const char *name, ip_addr_t *addr) +{ + struct dns_api_msg msg; + err_t err; + sys_sem_t sem; + + LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;); + + err = sys_sem_new(&sem, 0); + if (err != ERR_OK) { + return err; + } + + msg.name = name; + msg.addr = addr; + msg.err = &err; + msg.sem = &sem; + + tcpip_callback(do_gethostbyname, &msg); + sys_sem_wait(&sem); + sys_sem_free(&sem); + + return err; +} +#endif /* LWIP_DNS*/ + +#endif /* LWIP_NETCONN */ diff --git a/app/lwip/api/api_msg.c b/app/lwip/api/api_msg.c new file mode 100644 index 00000000..dcb07e9c --- /dev/null +++ b/app/lwip/api/api_msg.c @@ -0,0 +1,1540 @@ +/** + * @file + * Sequential API Internal module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api_msg.h" + +#include "lwip/ip.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" + +#include "lwip/memp.h" +#include "lwip/tcpip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" + +#include + +#define SET_NONBLOCKING_CONNECT(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0) +#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0) + +/* forward declarations */ +#if LWIP_TCP +static err_t do_writemore(struct netconn *conn); +static void do_close_internal(struct netconn *conn); +#endif + +#if LWIP_RAW +/** + * Receive callback function for RAW netconns. + * Doesn't 'eat' the packet, only references it and sends it to + * conn->recvmbox + * + * @see raw.h (struct raw_pcb.recv) for parameters and return value + */ +static u8_t +recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip_addr_t *addr) +{ + struct pbuf *q; + struct netbuf *buf; + struct netconn *conn; + + LWIP_UNUSED_ARG(addr); + conn = (struct netconn *)arg; + + if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) { +#if LWIP_SO_RCVBUF + int recv_avail; + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) { + return 0; + } +#endif /* LWIP_SO_RCVBUF */ + /* copy the whole packet into new pbufs */ + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(q != NULL) { + if (pbuf_copy(q, p) != ERR_OK) { + pbuf_free(q); + q = NULL; + } + } + + if (q != NULL) { + u16_t len; + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(q); + return 0; + } + + buf->p = q; + buf->ptr = q; + ip_addr_copy(buf->addr, *ip_current_src_addr()); + buf->port = pcb->protocol; + + len = q->tot_len; + if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return 0; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } + } + } + + return 0; /* do not eat the packet */ +} +#endif /* LWIP_RAW*/ + +#if LWIP_UDP +/** + * Receive callback function for UDP netconns. + * Posts the packet to conn->recvmbox or deletes it on memory error. + * + * @see udp.h (struct udp_pcb.recv) for parameters + */ +static void +recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *addr, u16_t port) +{ + struct netbuf *buf; + struct netconn *conn; + u16_t len; +#if LWIP_SO_RCVBUF + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + LWIP_UNUSED_ARG(pcb); /* only used for asserts... */ + LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_udp must have an argument", arg != NULL); + conn = (struct netconn *)arg; + LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb); + +#if LWIP_SO_RCVBUF + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) || + ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) { +#else /* LWIP_SO_RCVBUF */ + if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) { +#endif /* LWIP_SO_RCVBUF */ + pbuf_free(p); + return; + } + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(p); + return; + } else { + buf->p = p; + buf->ptr = p; + ip_addr_set(&buf->addr, addr); + buf->port = port; +#if LWIP_NETBUF_RECVINFO + { + const struct ip_hdr* iphdr = ip_current_header(); + /* get the UDP header - always in the first pbuf, ensured by udp_input */ + const struct udp_hdr* udphdr = (void*)(((char*)iphdr) + IPH_LEN(iphdr)); +#if LWIP_CHECKSUM_ON_COPY + buf->flags = NETBUF_FLAG_DESTADDR; +#endif /* LWIP_CHECKSUM_ON_COPY */ + ip_addr_set(&buf->toaddr, ip_current_dest_addr()); + buf->toport_chksum = udphdr->dest; + } +#endif /* LWIP_NETBUF_RECVINFO */ + } + + len = p->tot_len; + if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } +} +#endif /* LWIP_UDP */ + +#if LWIP_TCP +/** + * Receive callback function for TCP netconns. + * Posts the packet to conn->recvmbox, but doesn't delete it on errors. + * + * @see tcp.h (struct tcp_pcb.recv) for parameters and return value + */ +static err_t +recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + struct netconn *conn; + u16_t len; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_tcp must have an argument", arg != NULL); + conn = (struct netconn *)arg; + LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); + + if (conn == NULL) { + return ERR_VAL; + } + if (!sys_mbox_valid(&conn->recvmbox)) { + /* recvmbox already deleted */ + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } + return ERR_OK; + } + /* Unlike for UDP or RAW pcbs, don't check for available space + using recv_avail since that could break the connection + (data is already ACKed) */ + + /* don't overwrite fatal errors! */ + NETCONN_SET_SAFE_ERR(conn, err); + + if (p != NULL) { + len = p->tot_len; + } else { + len = 0; + } + + if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) { + /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */ + return ERR_MEM; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } + + return ERR_OK; +} + +/** + * Poll callback function for TCP netconns. + * Wakes up an application thread that waits for a connection to close + * or data to be sent. The application thread then takes the + * appropriate action to go on. + * + * Signals the conn->sem. + * netconn_close waits for conn->sem if closing failed. + * + * @see tcp.h (struct tcp_pcb.poll) for parameters and return value + */ +static err_t +poll_tcp(void *arg, struct tcp_pcb *pcb) +{ + struct netconn *conn = (struct netconn *)arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn->state == NETCONN_WRITE) { + do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + do_close_internal(conn); + } + /* @todo: implement connect timeout here? */ + + /* Did a nonblocking write fail before? Then check available write-space. */ + if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) { + /* If the queued byte- or pbuf-count drops below the configured low-water limit, + let select mark this pcb as writable again. */ + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + } + } + + return ERR_OK; +} + +/** + * Sent callback function for TCP netconns. + * Signals the conn->sem and calls API_EVENT. + * netconn_write waits for conn->sem if send buffer is low. + * + * @see tcp.h (struct tcp_pcb.sent) for parameters and return value + */ +static err_t +sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct netconn *conn = (struct netconn *)arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn->state == NETCONN_WRITE) { + do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + do_close_internal(conn); + } + + if (conn) { + /* If the queued byte- or pbuf-count drops below the configured low-water limit, + let select mark this pcb as writable again. */ + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; + API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); + } + } + + return ERR_OK; +} + +/** + * Error callback function for TCP netconns. + * Signals conn->sem, posts to all conn mboxes and calls API_EVENT. + * The application thread has then to decide what to do. + * + * @see tcp.h (struct tcp_pcb.err) for parameters + */ +static void +err_tcp(void *arg, err_t err) +{ + struct netconn *conn; + enum netconn_state old_state; + SYS_ARCH_DECL_PROTECT(lev); + + conn = (struct netconn *)arg; + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + conn->pcb.tcp = NULL; + + /* no check since this is always fatal! */ + SYS_ARCH_PROTECT(lev); + conn->last_err = err; + SYS_ARCH_UNPROTECT(lev); + + /* reset conn->state now before waking up other threads */ + old_state = conn->state; + conn->state = NETCONN_NONE; + + /* Notify the user layer about a connection error. Used to signal + select. */ + API_EVENT(conn, NETCONN_EVT_ERROR, 0); + /* Try to release selects pending on 'read' or 'write', too. + They will get an error if they actually try to read or write. */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + + /* pass NULL-message to recvmbox to wake up pending recv */ + if (sys_mbox_valid(&conn->recvmbox)) { + /* use trypost to prevent deadlock */ + sys_mbox_trypost(&conn->recvmbox, NULL); + } + /* pass NULL-message to acceptmbox to wake up pending accept */ + if (sys_mbox_valid(&conn->acceptmbox)) { + /* use trypost to preven deadlock */ + sys_mbox_trypost(&conn->acceptmbox, NULL); + } + + if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) || + (old_state == NETCONN_CONNECT)) { + /* calling do_writemore/do_close_internal is not necessary + since the pcb has already been deleted! */ + int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn); + SET_NONBLOCKING_CONNECT(conn, 0); + + if (!was_nonblocking_connect) { + /* set error return code */ + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + conn->current_msg->err = err; + conn->current_msg = NULL; + /* wake up the waiting task */ + sys_sem_signal(&conn->op_completed); + } + } else { + LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL); + } +} + +/** + * Setup a tcp_pcb with the correct callback function pointers + * and their arguments. + * + * @param conn the TCP netconn to setup + */ +static void +setup_tcp(struct netconn *conn) +{ + struct tcp_pcb *pcb; + + pcb = conn->pcb.tcp; + tcp_arg(pcb, conn); + tcp_recv(pcb, recv_tcp); + tcp_sent(pcb, sent_tcp); + tcp_poll(pcb, poll_tcp, 4); + tcp_err(pcb, err_tcp); +} + +/** + * Accept callback function for TCP netconns. + * Allocates a new netconn and posts that to conn->acceptmbox. + * + * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value + */ +static err_t +accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + struct netconn *newconn; + struct netconn *conn = (struct netconn *)arg; + + LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state))); + + if (!sys_mbox_valid(&conn->acceptmbox)) { + LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n")); + return ERR_VAL; + } + + /* We have to set the callback here even though + * the new socket is unknown. conn->socket is marked as -1. */ + newconn = netconn_alloc(conn->type, conn->callback); + if (newconn == NULL) { + return ERR_MEM; + } + newconn->pcb.tcp = newpcb; + setup_tcp(newconn); + /* no protection: when creating the pcb, the netconn is not yet known + to the application thread */ + newconn->last_err = err; + + if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) { + /* When returning != ERR_OK, the pcb is aborted in tcp_process(), + so do nothing here! */ + newconn->pcb.tcp = NULL; + /* no need to drain since we know the recvmbox is empty. */ + sys_mbox_free(&newconn->recvmbox); + sys_mbox_set_invalid(&newconn->recvmbox); + netconn_free(newconn); + return ERR_MEM; + } else { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Create a new pcb of a specific type. + * Called from do_newconn(). + * + * @param msg the api_msg_msg describing the connection type + * @return msg->conn->err, but the return value is currently ignored + */ +static void +pcb_new(struct api_msg_msg *msg) +{ + LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL); + + /* Allocate a PCB for this connection */ + switch(NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->conn->pcb.raw = raw_new(msg->msg.n.proto); + if(msg->conn->pcb.raw == NULL) { + msg->err = ERR_MEM; + break; + } + raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->pcb.udp = udp_new(); + if(msg->conn->pcb.udp == NULL) { + msg->err = ERR_MEM; + break; + } +#if LWIP_UDPLITE + if (msg->conn->type==NETCONN_UDPLITE) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); + } +#endif /* LWIP_UDPLITE */ + if (msg->conn->type==NETCONN_UDPNOCHKSUM) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); + } + udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->conn->pcb.tcp = tcp_new(); + if(msg->conn->pcb.tcp == NULL) { + msg->err = ERR_MEM; + break; + } + setup_tcp(msg->conn); + break; +#endif /* LWIP_TCP */ + default: + /* Unsupported netconn type, e.g. protocol disabled */ + msg->err = ERR_VAL; + break; + } +} + +/** + * Create a new pcb of a specific type inside a netconn. + * Called from netconn_new_with_proto_and_callback. + * + * @param msg the api_msg_msg describing the connection type + */ +void +do_newconn(struct api_msg_msg *msg) +{ + msg->err = ERR_OK; + if(msg->conn->pcb.tcp == NULL) { + pcb_new(msg); + } + /* Else? This "new" connection already has a PCB allocated. */ + /* Is this an error condition? Should it be deleted? */ + /* We currently just are happy and return. */ + + TCPIP_APIMSG_ACK(msg); +} + +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is NOT created! + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_alloc(enum netconn_type t, netconn_callback callback) +{ + struct netconn *conn; + int size; + + conn = (struct netconn *)memp_malloc(MEMP_NETCONN); + if (conn == NULL) { + return NULL; + } + + conn->last_err = ERR_OK; + conn->type = t; + conn->pcb.tcp = NULL; + +#if (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_UDP_RECVMBOX_SIZE) && \ + (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_TCP_RECVMBOX_SIZE) + size = DEFAULT_RAW_RECVMBOX_SIZE; +#else + switch(NETCONNTYPE_GROUP(t)) { +#if LWIP_RAW + case NETCONN_RAW: + size = DEFAULT_RAW_RECVMBOX_SIZE; + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + size = DEFAULT_UDP_RECVMBOX_SIZE; + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + size = DEFAULT_TCP_RECVMBOX_SIZE; + break; +#endif /* LWIP_TCP */ + default: + LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0); + break; + } +#endif + + if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { + memp_free(MEMP_NETCONN, conn); + return NULL; + } + if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) { + sys_sem_free(&conn->op_completed); + memp_free(MEMP_NETCONN, conn); + return NULL; + } + +#if LWIP_TCP + sys_mbox_set_invalid(&conn->acceptmbox); +#endif + conn->state = NETCONN_NONE; +#if LWIP_SOCKET + /* initialize socket to -1 since 0 is a valid socket */ + conn->socket = -1; +#endif /* LWIP_SOCKET */ + conn->callback = callback; +#if LWIP_TCP + conn->current_msg = NULL; + conn->write_offset = 0; +#endif /* LWIP_TCP */ +#if LWIP_SO_RCVTIMEO + conn->recv_timeout = 0; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + conn->recv_bufsize = RECV_BUFSIZE_DEFAULT; + conn->recv_avail = 0; +#endif /* LWIP_SO_RCVBUF */ + conn->flags = 0; + return conn; +} + +/** + * Delete a netconn and all its resources. + * The pcb is NOT freed (since we might not be in the right thread context do this). + * + * @param conn the netconn to free + */ +void +netconn_free(struct netconn *conn) +{ + LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL); + LWIP_ASSERT("recvmbox must be deallocated before calling this function", + !sys_mbox_valid(&conn->recvmbox)); +#if LWIP_TCP + LWIP_ASSERT("acceptmbox must be deallocated before calling this function", + !sys_mbox_valid(&conn->acceptmbox)); +#endif /* LWIP_TCP */ + + sys_sem_free(&conn->op_completed); + sys_sem_set_invalid(&conn->op_completed); + + memp_free(MEMP_NETCONN, conn); +} + +/** + * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in + * these mboxes + * + * @param conn the netconn to free + * @bytes_drained bytes drained from recvmbox + * @accepts_drained pending connections drained from acceptmbox + */ +static void +netconn_drain(struct netconn *conn) +{ + void *mem; +#if LWIP_TCP + struct pbuf *p; +#endif /* LWIP_TCP */ + + /* This runs in tcpip_thread, so we don't need to lock against rx packets */ + + /* Delete and drain the recvmbox. */ + if (sys_mbox_valid(&conn->recvmbox)) { + while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) { +#if LWIP_TCP + if (conn->type == NETCONN_TCP) { + if(mem != NULL) { + p = (struct pbuf*)mem; + /* pcb might be set to NULL already by err_tcp() */ + if (conn->pcb.tcp != NULL) { + tcp_recved(conn->pcb.tcp, p->tot_len); + } + pbuf_free(p); + } + } else +#endif /* LWIP_TCP */ + { + netbuf_delete((struct netbuf *)mem); + } + } + sys_mbox_free(&conn->recvmbox); + sys_mbox_set_invalid(&conn->recvmbox); + } + + /* Delete and drain the acceptmbox. */ +#if LWIP_TCP + if (sys_mbox_valid(&conn->acceptmbox)) { + while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) { + struct netconn *newconn = (struct netconn *)mem; + /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */ + /* pcb might be set to NULL already by err_tcp() */ + if (conn->pcb.tcp != NULL) { + tcp_accepted(conn->pcb.tcp); + } + /* drain recvmbox */ + netconn_drain(newconn); + if (newconn->pcb.tcp != NULL) { + tcp_abort(newconn->pcb.tcp); + newconn->pcb.tcp = NULL; + } + netconn_free(newconn); + } + sys_mbox_free(&conn->acceptmbox); + sys_mbox_set_invalid(&conn->acceptmbox); + } +#endif /* LWIP_TCP */ +} + +#if LWIP_TCP +/** + * Internal helper function to close a TCP netconn: since this sometimes + * doesn't work at the first attempt, this function is called from multiple + * places. + * + * @param conn the TCP netconn to close + */ +static void +do_close_internal(struct netconn *conn) +{ + err_t err; + u8_t shut, shut_rx, shut_tx, close; + + LWIP_ASSERT("invalid conn", (conn != NULL)); + LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP)); + LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE)); + LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL)); + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + + shut = conn->current_msg->msg.sd.shut; + shut_rx = shut & NETCONN_SHUT_RD; + shut_tx = shut & NETCONN_SHUT_WR; + /* shutting down both ends is the same as closing */ + close = shut == NETCONN_SHUT_RDWR; + + /* Set back some callback pointers */ + if (close) { + tcp_arg(conn->pcb.tcp, NULL); + } + if (conn->pcb.tcp->state == LISTEN) { + tcp_accept(conn->pcb.tcp, NULL); + } else { + /* some callbacks have to be reset if tcp_close is not successful */ + if (shut_rx) { + tcp_recv(conn->pcb.tcp, NULL); + tcp_accept(conn->pcb.tcp, NULL); + } + if (shut_tx) { + tcp_sent(conn->pcb.tcp, NULL); + } + if (close) { + tcp_poll(conn->pcb.tcp, NULL, 4); + tcp_err(conn->pcb.tcp, NULL); + } + } + /* Try to close the connection */ + if (shut == NETCONN_SHUT_RDWR) { + err = tcp_close(conn->pcb.tcp); + } else { + err = tcp_shutdown(conn->pcb.tcp, shut & NETCONN_SHUT_RD, shut & NETCONN_SHUT_WR); + } + if (err == ERR_OK) { + /* Closing succeeded */ + conn->current_msg->err = ERR_OK; + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + /* Set back some callback pointers as conn is going away */ + conn->pcb.tcp = NULL; + /* Trigger select() in socket layer. Make sure everybody notices activity + on the connection, error first! */ + if (close) { + API_EVENT(conn, NETCONN_EVT_ERROR, 0); + } + if (shut_rx) { + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + if (shut_tx) { + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + } + /* wake up the application task */ + sys_sem_signal(&conn->op_completed); + } else { + /* Closing failed, restore some of the callbacks */ + /* Closing of listen pcb will never fail! */ + LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN)); + tcp_sent(conn->pcb.tcp, sent_tcp); + tcp_poll(conn->pcb.tcp, poll_tcp, 4); + tcp_err(conn->pcb.tcp, err_tcp); + tcp_arg(conn->pcb.tcp, conn); + /* don't restore recv callback: we don't want to receive any more data */ + } + /* If closing didn't succeed, we get called again either + from poll_tcp or from sent_tcp */ +} +#endif /* LWIP_TCP */ + +/** + * Delete the pcb inside a netconn. + * Called from netconn_delete. + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_delconn(struct api_msg_msg *msg) +{ + /* @todo TCP: abort running write/connect? */ + if ((msg->conn->state != NETCONN_NONE) && + (msg->conn->state != NETCONN_LISTEN) && + (msg->conn->state != NETCONN_CONNECT)) { + /* this only happens for TCP netconns */ + LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP); + msg->err = ERR_INPROGRESS; + } else { + LWIP_ASSERT("blocking connect in progress", + (msg->conn->state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn)); + /* Drain and delete mboxes */ + netconn_drain(msg->conn); + + if (msg->conn->pcb.tcp != NULL) { + + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + raw_remove(msg->conn->pcb.raw); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->pcb.udp->recv_arg = NULL; + udp_remove(msg->conn->pcb.udp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->state = NETCONN_CLOSE; + msg->msg.sd.shut = NETCONN_SHUT_RDWR; + msg->conn->current_msg = msg; + do_close_internal(msg->conn); + /* API_EVENT is called inside do_close_internal, before releasing + the application thread, so we can return at this point! */ + return; +#endif /* LWIP_TCP */ + default: + break; + } + msg->conn->pcb.tcp = NULL; + } + /* tcp netconns don't come here! */ + + /* @todo: this lets select make the socket readable and writable, + which is wrong! errfd instead? */ + API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0); + } + if (sys_sem_valid(&msg->conn->op_completed)) { + sys_sem_signal(&msg->conn->op_completed); + } +} + +/** + * Bind a pcb contained in a netconn + * Called from netconn_bind. + * + * @param msg the api_msg_msg pointing to the connection and containing + * the IP address and port to bind to + */ +void +do_bind(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_VAL; + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_TCP */ + default: + break; + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * TCP callback function if a connection (opened by tcp_connect/do_connect) has + * been established (or reset by the remote host). + * + * @see tcp.h (struct tcp_pcb.connected) for parameters and return values + */ +static err_t +do_connected(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct netconn *conn; + int was_blocking; + + LWIP_UNUSED_ARG(pcb); + + conn = (struct netconn *)arg; + + if (conn == NULL) { + return ERR_VAL; + } + + LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT); + LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect", + (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn)); + + if (conn->current_msg != NULL) { + conn->current_msg->err = err; + } + if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) { + setup_tcp(conn); + } + was_blocking = !IN_NONBLOCKING_CONNECT(conn); + SET_NONBLOCKING_CONNECT(conn, 0); + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + if (!was_blocking) { + SYS_ARCH_DECL_PROTECT(lev); + SYS_ARCH_PROTECT(lev); + if (conn->last_err == ERR_INPROGRESS) { + conn->last_err = ERR_OK; + } + SYS_ARCH_UNPROTECT(lev); + } + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + + if (was_blocking) { + sys_sem_signal(&conn->op_completed); + } + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Connect a pcb contained inside a netconn + * Called from netconn_connect. + * + * @param msg the api_msg_msg pointing to the connection and containing + * the IP address and port to connect to + */ +void +do_connect(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.tcp == NULL) { + /* This may happen when calling netconn_connect() a second time */ + msg->err = ERR_CLSD; + } else { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + /* Prevent connect while doing any other action. */ + if (msg->conn->state != NETCONN_NONE) { + msg->err = ERR_ISCONN; + } else { + setup_tcp(msg->conn); + msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, + msg->msg.bc.port, do_connected); + if (msg->err == ERR_OK) { + u8_t non_blocking = netconn_is_nonblocking(msg->conn); + msg->conn->state = NETCONN_CONNECT; + SET_NONBLOCKING_CONNECT(msg->conn, non_blocking); + if (non_blocking) { + msg->err = ERR_INPROGRESS; + } else { + msg->conn->current_msg = msg; + /* sys_sem_signal() is called from do_connected (or err_tcp()), + * when the connection is established! */ + return; + } + } + } + break; +#endif /* LWIP_TCP */ + default: + LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0)); + break; + } + } + sys_sem_signal(&msg->conn->op_completed); +} + +/** + * Connect a pcb contained inside a netconn + * Only used for UDP netconns. + * Called from netconn_disconnect. + * + * @param msg the api_msg_msg pointing to the connection to disconnect + */ +void +do_disconnect(struct api_msg_msg *msg) +{ +#if LWIP_UDP + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { + udp_disconnect(msg->conn->pcb.udp); + msg->err = ERR_OK; + } else +#endif /* LWIP_UDP */ + { + msg->err = ERR_VAL; + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * Set a TCP pcb contained in a netconn into listen mode + * Called from netconn_listen. + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_listen(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { + if (msg->conn->type == NETCONN_TCP) { + if (msg->conn->state == NETCONN_NONE) { +#if TCP_LISTEN_BACKLOG + struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); +#else /* TCP_LISTEN_BACKLOG */ + struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp); +#endif /* TCP_LISTEN_BACKLOG */ + if (lpcb == NULL) { + /* in this case, the old pcb is still allocated */ + msg->err = ERR_MEM; + } else { + /* delete the recvmbox and allocate the acceptmbox */ + if (sys_mbox_valid(&msg->conn->recvmbox)) { + /** @todo: should we drain the recvmbox here? */ + sys_mbox_free(&msg->conn->recvmbox); + sys_mbox_set_invalid(&msg->conn->recvmbox); + } + msg->err = ERR_OK; + if (!sys_mbox_valid(&msg->conn->acceptmbox)) { + msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE); + } + if (msg->err == ERR_OK) { + msg->conn->state = NETCONN_LISTEN; + msg->conn->pcb.tcp = lpcb; + tcp_arg(msg->conn->pcb.tcp, msg->conn); + tcp_accept(msg->conn->pcb.tcp, accept_function); + } else { + /* since the old pcb is already deallocated, free lpcb now */ + tcp_close(lpcb); + msg->conn->pcb.tcp = NULL; + } + } + } + } + } + } + TCPIP_APIMSG_ACK(msg); +} +#endif /* LWIP_TCP */ + +/** + * Send some data on a RAW or UDP pcb contained in a netconn + * Called from netconn_send + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_send(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (ip_addr_isany(&msg->msg.b->addr)) { + msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p); + } else { + msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr); + } + break; +#endif +#if LWIP_UDP + case NETCONN_UDP: +#if LWIP_CHECKSUM_ON_COPY + if (ip_addr_isany(&msg->msg.b->addr)) { + msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p, + msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); + } else { + msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p, + &msg->msg.b->addr, msg->msg.b->port, + msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); + } +#else /* LWIP_CHECKSUM_ON_COPY */ + if (ip_addr_isany(&msg->msg.b->addr)) { + msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p); + } else { + msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port); + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + break; +#endif /* LWIP_UDP */ + default: + break; + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * Indicate data has been received from a TCP pcb contained in a netconn + * Called from netconn_recv + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_recv(struct api_msg_msg *msg) +{ + msg->err = ERR_OK; + if (msg->conn->pcb.tcp != NULL) { + if (msg->conn->type == NETCONN_TCP) { +#if TCP_LISTEN_BACKLOG + if (msg->conn->pcb.tcp->state == LISTEN) { + tcp_accepted(msg->conn->pcb.tcp); + } else +#endif /* TCP_LISTEN_BACKLOG */ + { + u32_t remaining = msg->msg.r.len; + do { + u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining; + tcp_recved(msg->conn->pcb.tcp, recved); + remaining -= recved; + }while(remaining != 0); + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * See if more data needs to be written from a previous call to netconn_write. + * Called initially from do_write. If the first call can't send all data + * (because of low memory or empty send-buffer), this function is called again + * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the + * blocking application thread (waiting in netconn_write) is released. + * + * @param conn netconn (that is currently in state NETCONN_WRITE) to process + * @return ERR_OK + * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished + */ +static err_t +do_writemore(struct netconn *conn) +{ + err_t err = ERR_OK; + void *dataptr; + u16_t len, available; + u8_t write_finished = 0; + size_t diff; + u8_t dontblock = netconn_is_nonblocking(conn) || + (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK); + u8_t apiflags = conn->current_msg->msg.w.apiflags; + + LWIP_ASSERT("conn != NULL", conn != NULL); + LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE)); + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL); + LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len", + conn->write_offset < conn->current_msg->msg.w.len); + + dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset; + diff = conn->current_msg->msg.w.len - conn->write_offset; + if (diff > 0xffffUL) { /* max_u16_t */ + len = 0xffff; +#if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; +#endif + apiflags |= TCP_WRITE_FLAG_MORE; + } else { + len = (u16_t)diff; + } + available = tcp_sndbuf(conn->pcb.tcp); + if (available < len) { + /* don't try to write more than sendbuf */ + len = available; +#if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; +#endif + apiflags |= TCP_WRITE_FLAG_MORE; + } + if (dontblock && (len < conn->current_msg->msg.w.len)) { + /* failed to send all data at once -> nonblocking write not possible */ + err = ERR_MEM; + } + if (err == ERR_OK) { + LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len)); + err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags); + } + if (dontblock && (err == ERR_MEM)) { + /* nonblocking write failed */ + write_finished = 1; + err = ERR_WOULDBLOCK; + /* let poll_tcp check writable space to mark the pcb + writable again */ + conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; + /* let select mark this pcb as non-writable. */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + } else { + /* if OK or memory error, check available space */ + if (((err == ERR_OK) || (err == ERR_MEM)) && + ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) || + (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT))) { + /* The queued byte- or pbuf-count exceeds the configured low-water limit, + let select mark this pcb as non-writable. */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + } + + if (err == ERR_OK) { + conn->write_offset += len; + if (conn->write_offset == conn->current_msg->msg.w.len) { + /* everything was written */ + write_finished = 1; + conn->write_offset = 0; + } + tcp_output(conn->pcb.tcp); + } else if (err == ERR_MEM) { + /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called + we do NOT return to the application thread, since ERR_MEM is + only a temporary error! */ + + /* tcp_write returned ERR_MEM, try tcp_output anyway */ + tcp_output(conn->pcb.tcp); + + #if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; + #endif + } else { + /* On errors != ERR_MEM, we don't try writing any more but return + the error to the application thread. */ + write_finished = 1; + } + } + + if (write_finished) { + /* everything was written: set back connection state + and back to application task */ + conn->current_msg->err = err; + conn->current_msg = NULL; + conn->state = NETCONN_NONE; +#if LWIP_TCPIP_CORE_LOCKING + if ((conn->flags & NETCONN_FLAG_WRITE_DELAYED) != 0) +#endif + { + sys_sem_signal(&conn->op_completed); + } + } +#if LWIP_TCPIP_CORE_LOCKING + else + return ERR_MEM; +#endif + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Send some data on a TCP pcb contained in a netconn + * Called from netconn_write + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_write(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + if (msg->conn->type == NETCONN_TCP) { +#if LWIP_TCP + if (msg->conn->state != NETCONN_NONE) { + /* netconn is connecting, closing or in blocking write */ + msg->err = ERR_INPROGRESS; + } else if (msg->conn->pcb.tcp != NULL) { + msg->conn->state = NETCONN_WRITE; + /* set all the variables used by do_writemore */ + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0); + msg->conn->current_msg = msg; + msg->conn->write_offset = 0; +#if LWIP_TCPIP_CORE_LOCKING + msg->conn->flags &= ~NETCONN_FLAG_WRITE_DELAYED; + if (do_writemore(msg->conn) != ERR_OK) { + LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE); + UNLOCK_TCPIP_CORE(); + sys_arch_sem_wait(&msg->conn->op_completed, 0); + LOCK_TCPIP_CORE(); + LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE); + } +#else /* LWIP_TCPIP_CORE_LOCKING */ + do_writemore(msg->conn); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + /* for both cases: if do_writemore was called, don't ACK the APIMSG + since do_writemore ACKs it! */ + return; + } else { + msg->err = ERR_CONN; + } +#else /* LWIP_TCP */ + msg->err = ERR_VAL; +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + } else { + msg->err = ERR_VAL; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Return a connection's local or remote address + * Called from netconn_getaddr + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_getaddr(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.ip != NULL) { + *(msg->msg.ad.ipaddr) = (msg->msg.ad.local ? msg->conn->pcb.ip->local_ip : + msg->conn->pcb.ip->remote_ip); + + msg->err = ERR_OK; + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (msg->msg.ad.local) { + *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol; + } else { + /* return an error as connecting is only a helper for upper layers */ + msg->err = ERR_CONN; + } + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + if (msg->msg.ad.local) { + *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port; + } else { + if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) { + msg->err = ERR_CONN; + } else { + *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port; + } + } + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port); + break; +#endif /* LWIP_TCP */ + default: + LWIP_ASSERT("invalid netconn_type", 0); + break; + } + } else { + msg->err = ERR_CONN; + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Close a TCP pcb contained in a netconn + * Called from netconn_close + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_close(struct api_msg_msg *msg) +{ +#if LWIP_TCP + /* @todo: abort running write/connect? */ + if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) { + /* this only happens for TCP netconns */ + LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP); + msg->err = ERR_INPROGRESS; + } else if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) { + if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) { + /* LISTEN doesn't support half shutdown */ + msg->err = ERR_CONN; + } else { + if (msg->msg.sd.shut & NETCONN_SHUT_RD) { + /* Drain and delete mboxes */ + netconn_drain(msg->conn); + } + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->state = NETCONN_CLOSE; + msg->conn->current_msg = msg; + do_close_internal(msg->conn); + /* for tcp netconns, do_close_internal ACKs the message */ + return; + } + } else +#endif /* LWIP_TCP */ + { + msg->err = ERR_VAL; + } + sys_sem_signal(&msg->conn->op_completed); +} + +#if LWIP_IGMP +/** + * Join multicast groups for UDP netconns. + * Called from netconn_join_leave_group + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_join_leave_group(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { +#if LWIP_UDP + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = igmp_joingroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr); + } else { + msg->err = igmp_leavegroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr); + } +#endif /* LWIP_UDP */ +#if (LWIP_TCP || LWIP_RAW) + } else { + msg->err = ERR_VAL; +#endif /* (LWIP_TCP || LWIP_RAW) */ + } + } else { + msg->err = ERR_CONN; + } + } + TCPIP_APIMSG_ACK(msg); +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Callback function that is called when DNS name is resolved + * (or on timeout). A waiting application thread is waked up by + * signaling the semaphore. + */ +static void +do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0); + LWIP_UNUSED_ARG(name); + + if (ipaddr == NULL) { + /* timeout or memory error */ + *msg->err = ERR_VAL; + } else { + /* address was resolved */ + *msg->err = ERR_OK; + *msg->addr = *ipaddr; + } + /* wake up the application task waiting in netconn_gethostbyname */ + sys_sem_signal(msg->sem); +} + +/** + * Execute a DNS query + * Called from netconn_gethostbyname + * + * @param arg the dns_api_msg pointing to the query + */ +void +do_gethostbyname(void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + *msg->err = dns_gethostbyname(msg->name, msg->addr, do_dns_found, msg); + if (*msg->err != ERR_INPROGRESS) { + /* on error or immediate success, wake up the application + * task waiting in netconn_gethostbyname */ + sys_sem_signal(msg->sem); + } +} +#endif /* LWIP_DNS */ + +#endif /* LWIP_NETCONN */ diff --git a/app/lwip/api/err.c b/app/lwip/api/err.c new file mode 100644 index 00000000..b0a4eb3e --- /dev/null +++ b/app/lwip/api/err.c @@ -0,0 +1,75 @@ +/** + * @file + * Error Management module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/err.h" + +#ifdef LWIP_DEBUG + +static const char *err_strerr[] = { + "Ok.", /* ERR_OK 0 */ + "Out of memory error.", /* ERR_MEM -1 */ + "Buffer error.", /* ERR_BUF -2 */ + "Timeout.", /* ERR_TIMEOUT -3 */ + "Routing problem.", /* ERR_RTE -4 */ + "Operation in progress.", /* ERR_INPROGRESS -5 */ + "Illegal value.", /* ERR_VAL -6 */ + "Operation would block.", /* ERR_WOULDBLOCK -7 */ + "Connection aborted.", /* ERR_ABRT -8 */ + "Connection reset.", /* ERR_RST -9 */ + "Connection closed.", /* ERR_CLSD -10 */ + "Not connected.", /* ERR_CONN -11 */ + "Illegal argument.", /* ERR_ARG -12 */ + "Address in use.", /* ERR_USE -13 */ + "Low-level netif error.", /* ERR_IF -14 */ + "Already connected.", /* ERR_ISCONN -15 */ +}; + +/** + * Convert an lwip internal error to a string representation. + * + * @param err an lwip internal err_t + * @return a string representation for err + */ +const char * +lwip_strerr(err_t err) +{ + return err_strerr[-err]; + +} + +#endif /* LWIP_DEBUG */ diff --git a/app/lwip/api/netbuf.c b/app/lwip/api/netbuf.c new file mode 100644 index 00000000..886f66b0 --- /dev/null +++ b/app/lwip/api/netbuf.c @@ -0,0 +1,245 @@ +/** + * @file + * Network buffer management + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netbuf.h" +#include "lwip/memp.h" + +#include + +/** + * Create (allocate) and initialize a new netbuf. + * The netbuf doesn't yet contain a packet buffer! + * һµnetbufռ䣬κݿռ + * @return a pointer to a new netbuf + * NULL on lack of memory + */ +struct +netbuf *netbuf_new(void) +{ + struct netbuf *buf; + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf != NULL) { + buf->p = NULL; + buf->ptr = NULL; + ip_addr_set_any(&buf->addr); + buf->port = 0; +#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY +#if LWIP_CHECKSUM_ON_COPY + buf->flags = 0; +#endif /* LWIP_CHECKSUM_ON_COPY */ + buf->toport_chksum = 0; +#if LWIP_NETBUF_RECVINFO + ip_addr_set_any(&buf->toaddr); +#endif /* LWIP_NETBUF_RECVINFO */ +#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ + return buf; + } else { + return NULL; + } +} + +/** + * Deallocate a netbuf allocated by netbuf_new(). + * ͷһnetbufռ + * @param buf pointer to a netbuf allocated by netbuf_new() + */ +void +netbuf_delete(struct netbuf *buf) +{ + if (buf != NULL) { + if (buf->p != NULL) { + pbuf_free(buf->p); + buf->p = buf->ptr = NULL; + } + memp_free(MEMP_NETBUF, buf); + } +} + +/** + * Allocate memory for a packet buffer for a given netbuf. + *ΪnetbufṹsizeСݿռ + * @param buf the netbuf for which to allocate a packet buffer + * @param size the size of the packet buffer to allocate + * @return pointer to the allocated memory + * NULL if no memory could be allocated + */ +void * +netbuf_alloc(struct netbuf *buf, u16_t size) +{ + LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;); + + /* Deallocate any previously allocated memory. */ + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM); + if (buf->p == NULL) { + return NULL; + } + LWIP_ASSERT("check that first pbuf can hold size", + (buf->p->len >= size)); + buf->ptr = buf->p; + return buf->p->payload; +} + +/** + * Free the packet buffer included in a netbuf + *ͷnetbufṹָpbuf + * @param buf pointer to the netbuf which contains the packet buffer to free + */ +void +netbuf_free(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = buf->ptr = NULL; +} + +/** + * Let a netbuf reference existing (non-volatile) data. + * + * @param buf netbuf which should reference the data + * @param dataptr pointer to the data to reference + * @param size size of the data + * @return ERR_OK if data is referenced + * ERR_MEM if data couldn't be referenced due to lack of memory + */ +err_t +netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size) +{ + LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); + if (buf->p == NULL) { + buf->ptr = NULL; + return ERR_MEM; + } + buf->p->payload = (void*)dataptr; + buf->p->len = buf->p->tot_len = size; + buf->ptr = buf->p; + return ERR_OK; +} + +/** + * Chain one netbuf to another (@see pbuf_chain) + * + * @param head the first netbuf + * @param tail netbuf to chain after head, freed by this function, may not be reference after returning + */ +void +netbuf_chain(struct netbuf *head, struct netbuf *tail) +{ + LWIP_ERROR("netbuf_ref: invalid head", (head != NULL), return;); + LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;); + pbuf_cat(head->p, tail->p); + head->ptr = head->p; + memp_free(MEMP_NETBUF, tail); +} + +/** + * Get the data pointer and length of the data inside a netbuf. + * + * @param buf netbuf to get the data from + * @param dataptr pointer to a void pointer where to store the data pointer + * @param len pointer to an u16_t where the length of the data is stored + * @return ERR_OK if the information was retreived, + * ERR_BUF on error. + */ +err_t +netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len) +{ + LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;); + + if (buf->ptr == NULL) { + return ERR_BUF; + } + *dataptr = buf->ptr->payload; + *len = buf->ptr->len; + return ERR_OK; +} + +/** + * Move the current data pointer of a packet buffer contained in a netbuf + * to the next part. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + * @return -1 if there is no next part + * 1 if moved to the next part but now there is no next part + * 0 if moved to the next part and there are still more parts + */ +s8_t +netbuf_next(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return -1;); + if (buf->ptr->next == NULL) { + return -1; + } + buf->ptr = buf->ptr->next; + if (buf->ptr->next == NULL) { + return 1; + } + return 0; +} + +/** + * Move the current data pointer of a packet buffer contained in a netbuf + * to the beginning of the packet. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + */ +void +netbuf_first(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + buf->ptr = buf->p; +} + +#endif /* LWIP_NETCONN */ diff --git a/app/lwip/api/netdb.c b/app/lwip/api/netdb.c new file mode 100644 index 00000000..a7e4e06b --- /dev/null +++ b/app/lwip/api/netdb.c @@ -0,0 +1,352 @@ +/** + * @file + * API functions for name resolving + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/netdb.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include "lwip/err.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/api.h" +#include "lwip/dns.h" + +#include +#include + +/** helper struct for gethostbyname_r to access the char* buffer */ +struct gethostbyname_r_helper { + ip_addr_t *addrs; + ip_addr_t addr; + char *aliases; +}; + +/** h_errno is exported in netdb.h for access by applications. */ +#if LWIP_DNS_API_DECLARE_H_ERRNO +int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */ + +/** define "hostent" variables storage: 0 if we use a static (but unprotected) + * set of variables for lwip_gethostbyname, 1 if we use a local storage */ +#ifndef LWIP_DNS_API_HOSTENT_STORAGE +#define LWIP_DNS_API_HOSTENT_STORAGE 0 +#endif + +/** define "hostent" variables storage */ +#if LWIP_DNS_API_HOSTENT_STORAGE +#define HOSTENT_STORAGE +#else +#define HOSTENT_STORAGE static +#endif /* LWIP_DNS_API_STATIC_HOSTENT */ + +/** + * Returns an entry containing addresses of address family AF_INET + * for the host with name name. + * Due to dns_gethostbyname limitations, only one address is returned. + * + * @param name the hostname to resolve + * @return an entry containing addresses of address family AF_INET + * for the host with name name + */ +struct hostent* +lwip_gethostbyname(const char *name) +{ + err_t err; + ip_addr_t addr; + + /* buffer variables for lwip_gethostbyname() */ + HOSTENT_STORAGE struct hostent s_hostent; + HOSTENT_STORAGE char *s_aliases; + HOSTENT_STORAGE ip_addr_t s_hostent_addr; + HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2]; + + /* query host IP address */ + err = netconn_gethostbyname(name, &addr); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + h_errno = HOST_NOT_FOUND; + return NULL; + } + + /* fill hostent */ + s_hostent_addr = addr; + s_phostent_addr[0] = &s_hostent_addr; + s_phostent_addr[1] = NULL; + s_hostent.h_name = (char*)name; + s_hostent.h_aliases = &s_aliases; + s_hostent.h_addrtype = AF_INET; + s_hostent.h_length = sizeof(ip_addr_t); + s_hostent.h_addr_list = (char**)&s_phostent_addr; + +#if DNS_DEBUG + /* dump hostent */ + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", s_hostent.h_aliases)); + if (s_hostent.h_aliases != NULL) { + u8_t idx; + for ( idx=0; s_hostent.h_aliases[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %p\n", idx, s_hostent.h_aliases[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %s\n", idx, s_hostent.h_aliases[idx])); + } + } + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", s_hostent.h_addr_list)); + if (s_hostent.h_addr_list != NULL) { + u8_t idx; + for ( idx=0; s_hostent.h_addr_list[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ip_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx]))); + } + } +#endif /* DNS_DEBUG */ + +#if LWIP_DNS_API_HOSTENT_STORAGE + /* this function should return the "per-thread" hostent after copy from s_hostent */ + return sys_thread_hostent(&s_hostent); +#else + return &s_hostent; +#endif /* LWIP_DNS_API_HOSTENT_STORAGE */ +} + +/** + * Thread-safe variant of lwip_gethostbyname: instead of using a static + * buffer, this function takes buffer and errno pointers as arguments + * and uses these for the result. + * + * @param name the hostname to resolve + * @param ret pre-allocated struct where to store the result + * @param buf pre-allocated buffer where to store additional data + * @param buflen the size of buf + * @param result pointer to a hostent pointer that is set to ret on success + * and set to zero on error + * @param h_errnop pointer to an int where to store errors (instead of modifying + * the global h_errno) + * @return 0 on success, non-zero on error, additional error information + * is stored in *h_errnop instead of h_errno to be thread-safe + */ +int +lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop) +{ + err_t err; + struct gethostbyname_r_helper *h; + char *hostname; + size_t namelen; + int lh_errno; + + if (h_errnop == NULL) { + /* ensure h_errnop is never NULL */ + h_errnop = &lh_errno; + } + + if (result == NULL) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + /* first thing to do: set *result to nothing */ + *result = NULL; + if ((name == NULL) || (ret == NULL) || (buf == 0)) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + + namelen = strlen(name); + if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) { + /* buf can't hold the data needed + a copy of name */ + *h_errnop = ERANGE; + return -1; + } + + h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf); + hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper); + + /* query host IP address */ + err = netconn_gethostbyname(name, &(h->addr)); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + *h_errnop = ENSRNOTFOUND; + return -1; + } + + /* copy the hostname into buf */ + MEMCPY(hostname, name, namelen); + hostname[namelen] = 0; + + /* fill hostent */ + h->addrs = &(h->addr); + h->aliases = NULL; + ret->h_name = (char*)hostname; + ret->h_aliases = &(h->aliases); + ret->h_addrtype = AF_INET; + ret->h_length = sizeof(ip_addr_t); + ret->h_addr_list = (char**)&(h->addrs); + + /* set result != NULL */ + *result = ret; + + /* return success */ + return 0; +} + +/** + * Frees one or more addrinfo structures returned by getaddrinfo(), along with + * any additional storage associated with those structures. If the ai_next field + * of the structure is not null, the entire list of structures is freed. + * + * @param ai struct addrinfo to free + */ +void +lwip_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + while (ai != NULL) { + next = ai->ai_next; + memp_free(MEMP_NETDB, ai); + ai = next; + } +} + +/** + * Translates the name of a service location (for example, a host name) and/or + * a service name and returns a set of socket addresses and associated + * information to be used in creating a socket with which to address the + * specified service. + * Memory for the result is allocated internally and must be freed by calling + * lwip_freeaddrinfo()! + * + * Due to a limitation in dns_gethostbyname, only the first address of a + * host is returned. + * Also, service names are not supported (only port numbers)! + * + * @param nodename descriptive name or address string of the host + * (may be NULL -> local address) + * @param servname port number as string of NULL + * @param hints structure containing input values that set socktype and protocol + * @param res pointer to a pointer where to store the result (set to NULL on failure) + * @return 0 on success, non-zero on failure + */ +int +lwip_getaddrinfo(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + err_t err; + ip_addr_t addr; + struct addrinfo *ai; + struct sockaddr_in *sa = NULL; + int port_nr = 0; + size_t total_size; + size_t namelen = 0; + + if (res == NULL) { + return EAI_FAIL; + } + *res = NULL; + if ((nodename == NULL) && (servname == NULL)) { + return EAI_NONAME; + } + + if (servname != NULL) { + /* service name specified: convert to port number + * @todo?: currently, only ASCII integers (port numbers) are supported! */ + port_nr = atoi(servname); + if ((port_nr <= 0) || (port_nr > 0xffff)) { + return EAI_SERVICE; + } + } + + if (nodename != NULL) { + /* service location specified, try to resolve */ + err = netconn_gethostbyname(nodename, &addr); + if (err != ERR_OK) { + return EAI_FAIL; + } + } else { + /* service location specified, use loopback address */ + ip_addr_set_loopback(&addr); + } + + total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_in); + if (nodename != NULL) { + namelen = strlen(nodename); + LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1); + total_size += namelen + 1; + } + /* If this fails, please report to lwip-devel! :-) */ + LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!", + total_size <= NETDB_ELEM_SIZE); + ai = (struct addrinfo *)memp_malloc(MEMP_NETDB); + if (ai == NULL) { + goto memerr; + } + memset(ai, 0, total_size); + sa = (struct sockaddr_in*)((u8_t*)ai + sizeof(struct addrinfo)); + /* set up sockaddr */ + inet_addr_from_ipaddr(&sa->sin_addr, &addr); + sa->sin_family = AF_INET; + sa->sin_len = sizeof(struct sockaddr_in); + sa->sin_port = htons((u16_t)port_nr); + + /* set up addrinfo */ + ai->ai_family = AF_INET; + if (hints != NULL) { + /* copy socktype & protocol from hints if specified */ + ai->ai_socktype = hints->ai_socktype; + ai->ai_protocol = hints->ai_protocol; + } + if (nodename != NULL) { + /* copy nodename to canonname if specified */ + ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_in)); + MEMCPY(ai->ai_canonname, nodename, namelen); + ai->ai_canonname[namelen] = 0; + } + ai->ai_addrlen = sizeof(struct sockaddr_in); + ai->ai_addr = (struct sockaddr*)sa; + + *res = ai; + + return 0; +memerr: + if (ai != NULL) { + memp_free(MEMP_NETDB, ai); + } + return EAI_MEMORY; +} + +#endif /* LWIP_DNS && LWIP_SOCKET */ diff --git a/app/lwip/api/netifapi.c b/app/lwip/api/netifapi.c new file mode 100644 index 00000000..43e47203 --- /dev/null +++ b/app/lwip/api/netifapi.c @@ -0,0 +1,160 @@ +/** + * @file + * Network Interface Sequential API module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netifapi.h" +#include "lwip/tcpip.h" + +/** + * Call netif_add() inside the tcpip_thread context. + */ +void +do_netifapi_netif_add(struct netifapi_msg_msg *msg) +{ + if (!netif_add( msg->netif, + msg->msg.add.ipaddr, + msg->msg.add.netmask, + msg->msg.add.gw, + msg->msg.add.state, + msg->msg.add.init, + msg->msg.add.input)) { + msg->err = ERR_IF; + } else { + msg->err = ERR_OK; + } + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call netif_set_addr() inside the tcpip_thread context. + */ +void +do_netifapi_netif_set_addr(struct netifapi_msg_msg *msg) +{ + netif_set_addr( msg->netif, + msg->msg.add.ipaddr, + msg->msg.add.netmask, + msg->msg.add.gw); + msg->err = ERR_OK; + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the + * tcpip_thread context. + */ +void +do_netifapi_netif_common(struct netifapi_msg_msg *msg) +{ + if (msg->msg.common.errtfunc != NULL) { + msg->err = msg->msg.common.errtfunc(msg->netif); + } else { + msg->err = ERR_OK; + msg->msg.common.voidfunc(msg->netif); + } + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call netif_add() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_add() + */ +err_t +netifapi_netif_add(struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw, + void *state, + netif_init_fn init, + netif_input_fn input) +{ + struct netifapi_msg msg; + msg.function = do_netifapi_netif_add; + msg.msg.netif = netif; + msg.msg.msg.add.ipaddr = ipaddr; + msg.msg.msg.add.netmask = netmask; + msg.msg.msg.add.gw = gw; + msg.msg.msg.add.state = state; + msg.msg.msg.add.init = init; + msg.msg.msg.add.input = input; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +/** + * Call netif_set_addr() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_set_addr() + */ +err_t +netifapi_netif_set_addr(struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw) +{ + struct netifapi_msg msg; + msg.function = do_netifapi_netif_set_addr; + msg.msg.netif = netif; + msg.msg.msg.add.ipaddr = ipaddr; + msg.msg.msg.add.netmask = netmask; + msg.msg.msg.add.gw = gw; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +/** + * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe + * way by running that function inside the tcpip_thread context. + * + * @note use only for functions where there is only "netif" parameter. + */ +err_t +netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc, + netifapi_errt_fn errtfunc) +{ + struct netifapi_msg msg; + msg.function = do_netifapi_netif_common; + msg.msg.netif = netif; + msg.msg.msg.common.voidfunc = voidfunc; + msg.msg.msg.common.errtfunc = errtfunc; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +#endif /* LWIP_NETIF_API */ diff --git a/app/lwip/api/sockets.c b/app/lwip/api/sockets.c new file mode 100644 index 00000000..f3afd630 --- /dev/null +++ b/app/lwip/api/sockets.c @@ -0,0 +1,2343 @@ +/** + * @file + * Sockets BSD-Like API module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * Improved by Marc Boucher and David Haas + * + */ + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sockets.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/inet.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcpip.h" +#include "lwip/pbuf.h" +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +#define NUM_SOCKETS MEMP_NUM_NETCONN + +/** Contains all internal pointers and states used for a socket */ +struct lwip_sock { + /** sockets currently are built on netconns, each socket has one netconn */ + struct netconn *conn; + /** data that was left from the previous read */ + void *lastdata; + /** offset in the data that was left from the previous read */ + u16_t lastoffset; + /** number of times data was received, set by event_callback(), + tested by the receive and select functions */ + s16_t rcvevent; + /** number of times data was ACKed (free send buffer), set by event_callback(), + tested by select */ + u16_t sendevent; + /** error happened for this socket, set by event_callback(), tested by select */ + u16_t errevent; + /** last error that occurred on this socket */ + int err; + /** counter of how many threads are waiting for this socket using select */ + int select_waiting; +}; + +/** Description for a task waiting in select */ +struct lwip_select_cb { + /** Pointer to the next waiting task */ + struct lwip_select_cb *next; + /** Pointer to the previous waiting task */ + struct lwip_select_cb *prev; + /** readset passed to select */ + fd_set *readset; + /** writeset passed to select */ + fd_set *writeset; + /** unimplemented: exceptset passed to select */ + fd_set *exceptset; + /** don't signal the same semaphore twice: set to 1 when signalled */ + int sem_signalled; + /** semaphore to wake up a task waiting for select */ + sys_sem_t sem; +}; + +/** This struct is used to pass data to the set/getsockopt_internal + * functions running in tcpip_thread context (only a void* is allowed) */ +struct lwip_setgetsockopt_data { + /** socket struct for which to change options */ + struct lwip_sock *sock; +#ifdef LWIP_DEBUG + /** socket index for which to change options */ + int s; +#endif /* LWIP_DEBUG */ + /** level of the option to process */ + int level; + /** name of the option to process */ + int optname; + /** set: value to set the option to + * get: value of the option is stored here */ + void *optval; + /** size of *optval */ + socklen_t *optlen; + /** if an error occures, it is temporarily stored here */ + err_t err; +}; + +/** The global array of available sockets */ +static struct lwip_sock sockets[NUM_SOCKETS]; +/** The global list of tasks waiting for select */ +static struct lwip_select_cb *select_cb_list; +/** This counter is increased from lwip_select when the list is chagned + and checked in event_callback to see if it has changed. */ +static volatile int select_cb_ctr; + +/** Table to quickly map an lwIP error (err_t) to a socket error + * by using -err as an index */ +static const int err_to_errno_table[] = { + 0, /* ERR_OK 0 No error, everything OK. */ + ENOMEM, /* ERR_MEM -1 Out of memory error. */ + ENOBUFS, /* ERR_BUF -2 Buffer error. */ + EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */ + EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */ + EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */ + EINVAL, /* ERR_VAL -6 Illegal value. */ + EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */ + ECONNABORTED, /* ERR_ABRT -8 Connection aborted. */ + ECONNRESET, /* ERR_RST -9 Connection reset. */ + ESHUTDOWN, /* ERR_CLSD -10 Connection closed. */ + ENOTCONN, /* ERR_CONN -11 Not connected. */ + EIO, /* ERR_ARG -12 Illegal argument. */ + EADDRINUSE, /* ERR_USE -13 Address in use. */ + -1, /* ERR_IF -14 Low-level netif error */ + -1, /* ERR_ISCONN -15 Already connected. */ +}; + +#define ERR_TO_ERRNO_TABLE_SIZE \ + (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0])) + +#define err_to_errno(err) \ + ((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \ + err_to_errno_table[-(err)] : EIO) + +#ifdef ERRNO +#ifndef set_errno +#define set_errno(err) errno = (err) +#endif +#else /* ERRNO */ +#define set_errno(err) +#endif /* ERRNO */ + +#define sock_set_errno(sk, e) do { \ + sk->err = (e); \ + set_errno(sk->err); \ +} while (0) + +/* Forward delcaration of some functions */ +static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len); +static void lwip_getsockopt_internal(void *arg); +static void lwip_setsockopt_internal(void *arg); + +/** + * Initialize this module. This function has to be called before any other + * functions in this module! + */ +void +lwip_socket_init(void) +{ +} + +/** + * Map a externally used socket index to the internal socket representation. + * + * @param s externally used socket index + * @return struct lwip_sock for the socket or NULL if not found + */ +static struct lwip_sock * +get_socket(int s) +{ + struct lwip_sock *sock; + + if ((s < 0) || (s >= NUM_SOCKETS)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s)); + set_errno(EBADF); + return NULL; + } + + sock = &sockets[s]; + + if (!sock->conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s)); + set_errno(EBADF); + return NULL; + } + + return sock; +} + +/** + * Same as get_socket but doesn't set errno + * + * @param s externally used socket index + * @return struct lwip_sock for the socket or NULL if not found + */ +static struct lwip_sock * +tryget_socket(int s) +{ + if ((s < 0) || (s >= NUM_SOCKETS)) { + return NULL; + } + if (!sockets[s].conn) { + return NULL; + } + return &sockets[s]; +} + +/** + * Allocate a new socket for a given netconn. + * + * @param newconn the netconn for which to allocate a socket + * @param accepted 1 if socket has been created by accept(), + * 0 if socket has been created by socket() + * @return the index of the new socket; -1 on error + */ +static int +alloc_socket(struct netconn *newconn, int accepted) +{ + int i; + SYS_ARCH_DECL_PROTECT(lev); + + /* allocate a new socket identifier */ + for (i = 0; i < NUM_SOCKETS; ++i) { + /* Protect socket array */ + SYS_ARCH_PROTECT(lev); + if (!sockets[i].conn) { + sockets[i].conn = newconn; + /* The socket is not yet known to anyone, so no need to protect + after having marked it as used. */ + SYS_ARCH_UNPROTECT(lev); + sockets[i].lastdata = NULL; + sockets[i].lastoffset = 0; + sockets[i].rcvevent = 0; + /* TCP sendbuf is empty, but the socket is not yet writable until connected + * (unless it has been created by accept()). */ + sockets[i].sendevent = (newconn->type == NETCONN_TCP ? (accepted != 0) : 1); + sockets[i].errevent = 0; + sockets[i].err = 0; + sockets[i].select_waiting = 0; + return i; + } + SYS_ARCH_UNPROTECT(lev); + } + return -1; +} + +/** Free a socket. The socket's netconn must have been + * delete before! + * + * @param sock the socket to free + * @param is_tcp != 0 for TCP sockets, used to free lastdata + */ +static void +free_socket(struct lwip_sock *sock, int is_tcp) +{ + void *lastdata; + SYS_ARCH_DECL_PROTECT(lev); + + lastdata = sock->lastdata; + sock->lastdata = NULL; + sock->lastoffset = 0; + sock->err = 0; + + /* Protect socket array */ + SYS_ARCH_PROTECT(lev); + sock->conn = NULL; + SYS_ARCH_UNPROTECT(lev); + /* don't use 'sock' after this line, as another task might have allocated it */ + + if (lastdata != NULL) { + if (is_tcp) { + pbuf_free((struct pbuf *)lastdata); + } else { + netbuf_delete((struct netbuf *)lastdata); + } + } +} + +/* Below this, the well-known socket functions are implemented. + * Use google.com or opengroup.org to get a good description :-) + * + * Exceptions are documented! + */ + +int +lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + struct lwip_sock *sock, *nsock; + struct netconn *newconn; + ip_addr_t naddr; + u16_t port; + int newsock; + struct sockaddr_in sin; + err_t err; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s)); + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s)); + sock_set_errno(sock, EWOULDBLOCK); + return -1; + } + + /* wait for a new connection */ + err = netconn_accept(sock->conn, &newconn); + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + LWIP_ASSERT("newconn != NULL", newconn != NULL); + /* Prevent automatic window updates, we do this on our own! */ + netconn_set_noautorecved(newconn, 1); + + /* get the IP address and port of the remote host */ + err = netconn_peer(newconn, &naddr, &port); + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err)); + netconn_delete(newconn); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + /* Note that POSIX only requires us to check addr is non-NULL. addrlen must + * not be NULL if addr is valid. + */ + if (NULL != addr) { + LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL); + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + inet_addr_from_ipaddr(&sin.sin_addr, &naddr); + + if (*addrlen > sizeof(sin)) + *addrlen = sizeof(sin); + + MEMCPY(addr, &sin, *addrlen); + } + + newsock = alloc_socket(newconn, 1); + if (newsock == -1) { + netconn_delete(newconn); + sock_set_errno(sock, ENFILE); + return -1; + } + LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS)); + LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback); + nsock = &sockets[newsock]; + + /* See event_callback: If data comes in right away after an accept, even + * though the server task might not have created a new socket yet. + * In that case, newconn->socket is counted down (newconn->socket--), + * so nsock->rcvevent is >= 1 here! + */ + SYS_ARCH_PROTECT(lev); + nsock->rcvevent += (s16_t)(-1 - newconn->socket); + newconn->socket = newsock; + SYS_ARCH_UNPROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock)); + ip_addr_debug_print(SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port)); + + sock_set_errno(sock, 0); + return newsock; +} + +int +lwip_bind(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct lwip_sock *sock; + ip_addr_t local_addr; + u16_t local_port; + err_t err; + const struct sockaddr_in *name_in; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* check size, familiy and alignment of 'name' */ + LWIP_ERROR("lwip_bind: invalid address", ((namelen == sizeof(struct sockaddr_in)) && + ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + name_in = (const struct sockaddr_in *)(void*)name; + + inet_addr_to_ipaddr(&local_addr, &name_in->sin_addr); + local_port = name_in->sin_port; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, &local_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(local_port))); + + err = netconn_bind(sock->conn, &local_addr, ntohs(local_port)); + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s)); + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_close(int s) +{ + struct lwip_sock *sock; + int is_tcp = 0; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if(sock->conn != NULL) { + is_tcp = netconn_type(sock->conn) == NETCONN_TCP; + } else { + LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL); + } + + netconn_delete(sock->conn); + + free_socket(sock, is_tcp); + set_errno(0); + return 0; +} + +int +lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct lwip_sock *sock; + err_t err; + const struct sockaddr_in *name_in; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* check size, familiy and alignment of 'name' */ + LWIP_ERROR("lwip_connect: invalid address", ((namelen == sizeof(struct sockaddr_in)) && + ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + name_in = (const struct sockaddr_in *)(void*)name; + + if (name_in->sin_family == AF_UNSPEC) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); + err = netconn_disconnect(sock->conn); + } else { + ip_addr_t remote_addr; + u16_t remote_port; + + inet_addr_to_ipaddr(&remote_addr, &name_in->sin_addr); + remote_port = name_in->sin_port; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(remote_port))); + + err = netconn_connect(sock->conn, &remote_addr, ntohs(remote_port)); + } + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s)); + sock_set_errno(sock, 0); + return 0; +} + +/** + * Set a socket into listen mode. + * The socket may not have been used for another connection previously. + * + * @param s the socket to set to listening mode + * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1) + * @return 0 on success, non-zero on failure + */ +int +lwip_listen(int s, int backlog) +{ + struct lwip_sock *sock; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* limit the "backlog" parameter to fit in an u8_t */ + backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff); + + err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog); + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen) +{ + struct lwip_sock *sock; + void *buf = NULL; + struct pbuf *p; + u16_t buflen, copylen; + int off = 0; + ip_addr_t *addr; + u16_t port; + u8_t done = 0; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags)); + sock = get_socket(s); + if (!sock) { + return -1; + } + + do { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata)); + /* Check if there is data left from the last recv operation. */ + if (sock->lastdata) { + buf = sock->lastdata; + } else { + /* If this is non-blocking call, then check first */ + if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) && + (sock->rcvevent <= 0)) { + if (off > 0) { + /* update receive window */ + netconn_recved(sock->conn, (u32_t)off); + /* already received data, return that */ + sock_set_errno(sock, 0); + return off; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s)); + sock_set_errno(sock, EWOULDBLOCK); + return -1; + } + + /* No data was left from the previous operation, so we try to get + some from the network. */ + if (netconn_type(sock->conn) == NETCONN_TCP) { + err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf); + } else { + err = netconn_recv(sock->conn, (struct netbuf **)&buf); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n", + err, buf)); + + if (err != ERR_OK) { + if (off > 0) { + /* update receive window */ + netconn_recved(sock->conn, (u32_t)off); + /* already received data, return that */ + sock_set_errno(sock, 0); + return off; + } + /* We should really do some error checking here. */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n", + s, lwip_strerr(err))); + sock_set_errno(sock, err_to_errno(err)); + if (err == ERR_CLSD) { + return 0; + } else { + return -1; + } + } + LWIP_ASSERT("buf != NULL", buf != NULL); + sock->lastdata = buf; + } + + if (netconn_type(sock->conn) == NETCONN_TCP) { + p = (struct pbuf *)buf; + } else { + p = ((struct netbuf *)buf)->p; + } + buflen = p->tot_len; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n", + buflen, len, off, sock->lastoffset)); + + buflen -= sock->lastoffset; + + if (len > buflen) { + copylen = buflen; + } else { + copylen = (u16_t)len; + } + + /* copy the contents of the received buffer into + the supplied memory pointer mem */ + pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset); + + off += copylen; + + if (netconn_type(sock->conn) == NETCONN_TCP) { + LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen); + len -= copylen; + if ( (len <= 0) || + (p->flags & PBUF_FLAG_PUSH) || + (sock->rcvevent <= 0) || + ((flags & MSG_PEEK)!=0)) { + done = 1; + } + } else { + done = 1; + } + + /* Check to see from where the data was.*/ + if (done) { + ip_addr_t fromaddr; + if (from && fromlen) { + struct sockaddr_in sin; + + if (netconn_type(sock->conn) == NETCONN_TCP) { + addr = &fromaddr; + netconn_getaddr(sock->conn, addr, &port, 0); + } else { + addr = netbuf_fromaddr((struct netbuf *)buf); + port = netbuf_fromport((struct netbuf *)buf); + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + inet_addr_from_ipaddr(&sin.sin_addr, addr); + + if (*fromlen > sizeof(sin)) { + *fromlen = sizeof(sin); + } + + MEMCPY(from, &sin, *fromlen); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); + } else { +#if SOCKETS_DEBUG + if (netconn_type(sock->conn) == NETCONN_TCP) { + addr = &fromaddr; + netconn_getaddr(sock->conn, addr, &port, 0); + } else { + addr = netbuf_fromaddr((struct netbuf *)buf); + port = netbuf_fromport((struct netbuf *)buf); + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); +#endif /* SOCKETS_DEBUG */ + } + } + + /* If we don't peek the incoming message... */ + if ((flags & MSG_PEEK) == 0) { + /* If this is a TCP socket, check if there is data left in the + buffer. If so, it should be saved in the sock structure for next + time around. */ + if ((netconn_type(sock->conn) == NETCONN_TCP) && (buflen - copylen > 0)) { + sock->lastdata = buf; + sock->lastoffset += copylen; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf)); + } else { + sock->lastdata = NULL; + sock->lastoffset = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf)); + if (netconn_type(sock->conn) == NETCONN_TCP) { + pbuf_free((struct pbuf *)buf); + } else { + netbuf_delete((struct netbuf *)buf); + } + } + } + } while (!done); + + if (off > 0) { + /* update receive window */ + netconn_recved(sock->conn, (u32_t)off); + } + sock_set_errno(sock, 0); + return off; +} + +int +lwip_read(int s, void *mem, size_t len) +{ + return lwip_recvfrom(s, mem, len, 0, NULL, NULL); +} + +int +lwip_recv(int s, void *mem, size_t len, int flags) +{ + return lwip_recvfrom(s, mem, len, flags, NULL, NULL); +} + +int +lwip_send(int s, const void *data, size_t size, int flags) +{ + struct lwip_sock *sock; + err_t err; + u8_t write_flags; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n", + s, data, size, flags)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (sock->conn->type != NETCONN_TCP) { +#if (LWIP_UDP || LWIP_RAW) + return lwip_sendto(s, data, size, flags, NULL, 0); +#else /* (LWIP_UDP || LWIP_RAW) */ + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + + if ((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) { + if ((size > TCP_SND_BUF) || ((size / TCP_MSS) > TCP_SND_QUEUELEN)) { + /* too much data to ever send nonblocking! */ + sock_set_errno(sock, EMSGSIZE); + return -1; + } + } + + write_flags = NETCONN_COPY | + ((flags & MSG_MORE) ? NETCONN_MORE : 0) | + ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0); + err = netconn_write(sock->conn, data, size, write_flags); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d size=%"SZT_F"\n", s, err, size)); + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? (int)size : -1); +} + +int +lwip_sendto(int s, const void *data, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen) +{ + struct lwip_sock *sock; + err_t err; + u16_t short_size; + const struct sockaddr_in *to_in; + u16_t remote_port; +#if !LWIP_TCPIP_CORE_LOCKING + struct netbuf buf; +#endif + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (sock->conn->type == NETCONN_TCP) { +#if LWIP_TCP + return lwip_send(s, data, size, flags); +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(flags); + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* LWIP_TCP */ + } + + /* @todo: split into multiple sendto's? */ + LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff); + short_size = (u16_t)size; + LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) || + ((tolen == sizeof(struct sockaddr_in)) && + ((to->sa_family) == AF_INET) && ((((mem_ptr_t)to) % 4) == 0))), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + to_in = (const struct sockaddr_in *)(void*)to; + +#if LWIP_TCPIP_CORE_LOCKING + /* Should only be consider like a sample or a simple way to experiment this option (no check of "to" field...) */ + { + struct pbuf* p; + ip_addr_t *remote_addr; + +#if LWIP_NETIF_TX_SINGLE_PBUF + p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_RAM); + if (p != NULL) { +#if LWIP_CHECKSUM_ON_COPY + u16_t chksum = 0; + if (sock->conn->type != NETCONN_RAW) { + chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size); + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + MEMCPY(p->payload, data, size); +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_REF); + if (p != NULL) { + p->payload = (void*)data; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + if (to_in != NULL) { + inet_addr_to_ipaddr_p(remote_addr, &to_in->sin_addr); + remote_port = ntohs(to_in->sin_port); + } else { + remote_addr = IP_ADDR_ANY; + remote_port = 0; + } + + LOCK_TCPIP_CORE(); + if (sock->conn->type == NETCONN_RAW) { + err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, remote_addr); + } else { +#if LWIP_UDP +#if LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF + err = sock->conn->last_err = udp_sendto_chksum(sock->conn->pcb.udp, p, + remote_addr, remote_port, 1, chksum); +#else /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */ + err = sock->conn->last_err = udp_sendto(sock->conn->pcb.udp, p, + remote_addr, remote_port); +#endif /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */ +#else /* LWIP_UDP */ + err = ERR_ARG; +#endif /* LWIP_UDP */ + } + UNLOCK_TCPIP_CORE(); + + pbuf_free(p); + } else { + err = ERR_MEM; + } + } +#else /* LWIP_TCPIP_CORE_LOCKING */ + /* initialize a buffer */ + buf.p = buf.ptr = NULL; +#if LWIP_CHECKSUM_ON_COPY + buf.flags = 0; +#endif /* LWIP_CHECKSUM_ON_COPY */ + if (to) { + inet_addr_to_ipaddr(&buf.addr, &to_in->sin_addr); + remote_port = ntohs(to_in->sin_port); + netbuf_fromport(&buf) = remote_port; + } else { + remote_port = 0; + ip_addr_set_any(&buf.addr); + netbuf_fromport(&buf) = 0; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%d"U16_F", flags=0x%x to=", + s, data, short_size, flags)); + ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port)); + + /* make the buffer point to the data that should be sent */ +#if LWIP_NETIF_TX_SINGLE_PBUF + /* Allocate a new netbuf and copy the data into it. */ + if (netbuf_alloc(&buf, short_size) == NULL) { + err = ERR_MEM; + } else { +#if LWIP_CHECKSUM_ON_COPY + if (sock->conn->type != NETCONN_RAW) { + u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size); + netbuf_set_chksum(&buf, chksum); + err = ERR_OK; + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + { + err = netbuf_take(&buf, data, short_size); + } + } +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + err = netbuf_ref(&buf, data, short_size); +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (err == ERR_OK) { + /* send the data */ + err = netconn_send(sock->conn, &buf); + } + + /* deallocated the buffer */ + netbuf_free(&buf); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? short_size : -1); +} + +int +lwip_socket(int domain, int type, int protocol) +{ + struct netconn *conn; + int i; + + LWIP_UNUSED_ARG(domain); + + /* create a netconn */ + switch (type) { + case SOCK_RAW: + conn = netconn_new_with_proto_and_callback(NETCONN_RAW, (u8_t)protocol, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + case SOCK_DGRAM: + conn = netconn_new_with_callback( (protocol == IPPROTO_UDPLITE) ? + NETCONN_UDPLITE : NETCONN_UDP, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + case SOCK_STREAM: + conn = netconn_new_with_callback(NETCONN_TCP, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + if (conn != NULL) { + /* Prevent automatic window updates, we do this on our own! */ + netconn_set_noautorecved(conn, 1); + } + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n", + domain, type, protocol)); + set_errno(EINVAL); + return -1; + } + + if (!conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n")); + set_errno(ENOBUFS); + return -1; + } + + i = alloc_socket(conn, 0); + + if (i == -1) { + netconn_delete(conn); + set_errno(ENFILE); + return -1; + } + conn->socket = i; + LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i)); + set_errno(0); + return i; +} + +int +lwip_write(int s, const void *data, size_t size) +{ + return lwip_send(s, data, size, 0); +} + +/** + * Go through the readset and writeset lists and see which socket of the sockets + * set in the sets has events. On return, readset, writeset and exceptset have + * the sockets enabled that had events. + * + * exceptset is not used for now!!! + * + * @param maxfdp1 the highest socket index in the sets + * @param readset_in: set of sockets to check for read events + * @param writeset_in: set of sockets to check for write events + * @param exceptset_in: set of sockets to check for error events + * @param readset_out: set of sockets that had read events + * @param writeset_out: set of sockets that had write events + * @param exceptset_out: set os sockets that had error events + * @return number of sockets that had events (read/write/exception) (>= 0) + */ +static int +lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in, + fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out) +{ + int i, nready = 0; + fd_set lreadset, lwriteset, lexceptset; + struct lwip_sock *sock; + SYS_ARCH_DECL_PROTECT(lev); + + FD_ZERO(&lreadset); + FD_ZERO(&lwriteset); + FD_ZERO(&lexceptset); + + /* Go through each socket in each list to count number of sockets which + currently match */ + for(i = 0; i < maxfdp1; i++) { + void* lastdata = NULL; + s16_t rcvevent = 0; + u16_t sendevent = 0; + u16_t errevent = 0; + /* First get the socket's status (protected)... */ + SYS_ARCH_PROTECT(lev); + sock = tryget_socket(i); + if (sock != NULL) { + lastdata = sock->lastdata; + rcvevent = sock->rcvevent; + sendevent = sock->sendevent; + errevent = sock->errevent; + } + SYS_ARCH_UNPROTECT(lev); + /* ... then examine it: */ + /* See if netconn of this socket is ready for read */ + if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) { + FD_SET(i, &lreadset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i)); + nready++; + } + /* See if netconn of this socket is ready for write */ + if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) { + FD_SET(i, &lwriteset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i)); + nready++; + } + /* See if netconn of this socket had an error */ + if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) { + FD_SET(i, &lexceptset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i)); + nready++; + } + } + /* copy local sets to the ones provided as arguments */ + *readset_out = lreadset; + *writeset_out = lwriteset; + *exceptset_out = lexceptset; + + LWIP_ASSERT("nready >= 0", nready >= 0); + return nready; +} + +/** + * Processing exceptset is not yet implemented. + */ +int +lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout) +{ + u32_t waitres = 0; + int nready; + fd_set lreadset, lwriteset, lexceptset; + u32_t msectimeout; + struct lwip_select_cb select_cb; + err_t err; + int i; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n", + maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset, + timeout ? (s32_t)timeout->tv_sec : (s32_t)-1, + timeout ? (s32_t)timeout->tv_usec : (s32_t)-1)); + + /* Go through each socket in each list to count number of sockets which + currently match */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + + /* If we don't have any current events, then suspend if we are supposed to */ + if (!nready) { + if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n")); + /* This is OK as the local fdsets are empty and nready is zero, + or we would have returned earlier. */ + goto return_copy_fdsets; + } + + /* None ready: add our semaphore to list: + We don't actually need any dynamic memory. Our entry on the + list is only valid while we are in this function, so it's ok + to use local variables. */ + + select_cb.next = NULL; + select_cb.prev = NULL; + select_cb.readset = readset; + select_cb.writeset = writeset; + select_cb.exceptset = exceptset; + select_cb.sem_signalled = 0; + err = sys_sem_new(&select_cb.sem, 0); + if (err != ERR_OK) { + /* failed to create semaphore */ + set_errno(ENOMEM); + return -1; + } + + /* Protect the select_cb_list */ + SYS_ARCH_PROTECT(lev); + + /* Put this select_cb on top of list */ + select_cb.next = select_cb_list; + if (select_cb_list != NULL) { + select_cb_list->prev = &select_cb; + } + select_cb_list = &select_cb; + /* Increasing this counter tells even_callback that the list has changed. */ + select_cb_ctr++; + + /* Now we can safely unprotect */ + SYS_ARCH_UNPROTECT(lev); + + /* Increase select_waiting for each socket we are interested in */ + for(i = 0; i < maxfdp1; i++) { + if ((readset && FD_ISSET(i, readset)) || + (writeset && FD_ISSET(i, writeset)) || + (exceptset && FD_ISSET(i, exceptset))) { + struct lwip_sock *sock = tryget_socket(i); + LWIP_ASSERT("sock != NULL", sock != NULL); + SYS_ARCH_PROTECT(lev); + sock->select_waiting++; + LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + SYS_ARCH_UNPROTECT(lev); + } + } + + /* Call lwip_selscan again: there could have been events between + the last scan (whithout us on the list) and putting us on the list! */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + if (!nready) { + /* Still none ready, just wait to be woken */ + if (timeout == 0) { + /* Wait forever */ + msectimeout = 0; + } else { + msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); + if (msectimeout == 0) { + /* Wait 1ms at least (0 means wait forever) */ + msectimeout = 1; + } + } + + waitres = sys_arch_sem_wait(&select_cb.sem, msectimeout); + } + /* Increase select_waiting for each socket we are interested in */ + for(i = 0; i < maxfdp1; i++) { + if ((readset && FD_ISSET(i, readset)) || + (writeset && FD_ISSET(i, writeset)) || + (exceptset && FD_ISSET(i, exceptset))) { + struct lwip_sock *sock = tryget_socket(i); + LWIP_ASSERT("sock != NULL", sock != NULL); + SYS_ARCH_PROTECT(lev); + sock->select_waiting--; + LWIP_ASSERT("sock->select_waiting >= 0", sock->select_waiting >= 0); + SYS_ARCH_UNPROTECT(lev); + } + } + /* Take us off the list */ + SYS_ARCH_PROTECT(lev); + if (select_cb.next != NULL) { + select_cb.next->prev = select_cb.prev; + } + if (select_cb_list == &select_cb) { + LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL); + select_cb_list = select_cb.next; + } else { + LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL); + select_cb.prev->next = select_cb.next; + } + /* Increasing this counter tells even_callback that the list has changed. */ + select_cb_ctr++; + SYS_ARCH_UNPROTECT(lev); + + sys_sem_free(&select_cb.sem); + if (waitres == SYS_ARCH_TIMEOUT) { + /* Timeout */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n")); + /* This is OK as the local fdsets are empty and nready is zero, + or we would have returned earlier. */ + goto return_copy_fdsets; + } + + /* See what's set */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready)); +return_copy_fdsets: + set_errno(0); + if (readset) { + *readset = lreadset; + } + if (writeset) { + *writeset = lwriteset; + } + if (exceptset) { + *exceptset = lexceptset; + } + + + return nready; +} + +/** + * Callback registered in the netconn layer for each socket-netconn. + * Processes recvevent (data available) and wakes up tasks waiting for select. + */ +static void +event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len) +{ + int s; + struct lwip_sock *sock; + struct lwip_select_cb *scb; + int last_select_cb_ctr; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_UNUSED_ARG(len); + + /* Get socket */ + if (conn) { + s = conn->socket; + if (s < 0) { + /* Data comes in right away after an accept, even though + * the server task might not have created a new socket yet. + * Just count down (or up) if that's the case and we + * will use the data later. Note that only receive events + * can happen before the new socket is set up. */ + SYS_ARCH_PROTECT(lev); + if (conn->socket < 0) { + if (evt == NETCONN_EVT_RCVPLUS) { + conn->socket--; + } + SYS_ARCH_UNPROTECT(lev); + return; + } + s = conn->socket; + SYS_ARCH_UNPROTECT(lev); + } + + sock = get_socket(s); + if (!sock) { + return; + } + } else { + return; + } + + SYS_ARCH_PROTECT(lev); + /* Set event as required */ + switch (evt) { + case NETCONN_EVT_RCVPLUS: + sock->rcvevent++; + break; + case NETCONN_EVT_RCVMINUS: + sock->rcvevent--; + break; + case NETCONN_EVT_SENDPLUS: + sock->sendevent = 1; + break; + case NETCONN_EVT_SENDMINUS: + sock->sendevent = 0; + break; + case NETCONN_EVT_ERROR: + sock->errevent = 1; + break; + default: + LWIP_ASSERT("unknown event", 0); + break; + } + + if (sock->select_waiting == 0) { + /* noone is waiting for this socket, no need to check select_cb_list */ + SYS_ARCH_UNPROTECT(lev); + return; + } + + /* Now decide if anyone is waiting for this socket */ + /* NOTE: This code goes through the select_cb_list list multiple times + ONLY IF a select was actually waiting. We go through the list the number + of waiting select calls + 1. This list is expected to be small. */ + + /* At this point, SYS_ARCH is still protected! */ +again: + for (scb = select_cb_list; scb != NULL; scb = scb->next) { + if (scb->sem_signalled == 0) { + /* semaphore not signalled yet */ + int do_signal = 0; + /* Test this select call for our socket */ + if (sock->rcvevent > 0) { + if (scb->readset && FD_ISSET(s, scb->readset)) { + do_signal = 1; + } + } + if (sock->sendevent != 0) { + if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) { + do_signal = 1; + } + } + if (sock->errevent != 0) { + if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) { + do_signal = 1; + } + } + if (do_signal) { + scb->sem_signalled = 1; + /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might + lead to the select thread taking itself off the list, invalidagin the semaphore. */ + sys_sem_signal(&scb->sem); + } + } + /* unlock interrupts with each step */ + last_select_cb_ctr = select_cb_ctr; + SYS_ARCH_UNPROTECT(lev); + /* this makes sure interrupt protection time is short */ + SYS_ARCH_PROTECT(lev); + if (last_select_cb_ctr != select_cb_ctr) { + /* someone has changed select_cb_list, restart at the beginning */ + goto again; + } + } + SYS_ARCH_UNPROTECT(lev); +} + +/** + * Unimplemented: Close one end of a full-duplex connection. + * Currently, the full connection is closed. + */ +int +lwip_shutdown(int s, int how) +{ + struct lwip_sock *sock; + err_t err; + u8_t shut_rx = 0, shut_tx = 0; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (sock->conn != NULL) { + if (netconn_type(sock->conn) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } + } else { + sock_set_errno(sock, ENOTCONN); + return ENOTCONN; + } + + if (how == SHUT_RD) { + shut_rx = 1; + } else if (how == SHUT_WR) { + shut_tx = 1; + } else if(how == SHUT_RDWR) { + shut_rx = 1; + shut_tx = 1; + } else { + sock_set_errno(sock, EINVAL); + return EINVAL; + } + err = netconn_shutdown(sock->conn, shut_rx, shut_tx); + + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? 0 : -1); +} + +static int +lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) +{ + struct lwip_sock *sock; + struct sockaddr_in sin; + ip_addr_t naddr; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + + /* get the IP address and port */ + netconn_getaddr(sock->conn, &naddr, &sin.sin_port, local); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", sin.sin_port)); + + sin.sin_port = htons(sin.sin_port); + inet_addr_from_ipaddr(&sin.sin_addr, &naddr); + + if (*namelen > sizeof(sin)) { + *namelen = sizeof(sin); + } + + MEMCPY(name, &sin, *namelen); + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 0); +} + +int +lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 1); +} + +int +lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) +{ + err_t err = ERR_OK; + struct lwip_sock *sock = get_socket(s); + struct lwip_setgetsockopt_data data; + + if (!sock) { + return -1; + } + + if ((NULL == optval) || (NULL == optlen)) { + sock_set_errno(sock, EFAULT); + return -1; + } + + /* Do length and type checks for the various options first, to keep it readable. */ + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + case SO_ACCEPTCONN: + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_ERROR: + case SO_KEEPALIVE: + /* UNIMPL case SO_CONTIMEO: */ + /* UNIMPL case SO_SNDTIMEO: */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: +#endif /* LWIP_SO_RCVBUF */ + /* UNIMPL case SO_OOBINLINE: */ + /* UNIMPL case SO_SNDBUF: */ + /* UNIMPL case SO_RCVLOWAT: */ + /* UNIMPL case SO_SNDLOWAT: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + case SO_TYPE: + /* UNIMPL case SO_USELOOPBACK: */ + if (*optlen < sizeof(int)) { + err = EINVAL; + } + break; + + case SO_NO_CHECK: + if (*optlen < sizeof(int)) { + err = EINVAL; + } +#if LWIP_UDP + if ((sock->conn->type != NETCONN_UDP) || + ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { + /* this flag is only available for UDP, not for UDP lite */ + err = EAFNOSUPPORT; + } +#endif /* LWIP_UDP */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + /* UNIMPL case IP_HDRINCL: */ + /* UNIMPL case IP_RCVDSTADDR: */ + /* UNIMPL case IP_RCVIF: */ + case IP_TTL: + case IP_TOS: + if (*optlen < sizeof(int)) { + err = EINVAL; + } + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + if (*optlen < sizeof(u8_t)) { + err = EINVAL; + } + break; + case IP_MULTICAST_IF: + if (*optlen < sizeof(struct in_addr)) { + err = EINVAL; + } + break; + case IP_MULTICAST_LOOP: + if (*optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; +#endif /* LWIP_IGMP */ + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + if (*optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no TCP socket, ignore any options. */ + if (sock->conn->type != NETCONN_TCP) + return 0; + + switch (optname) { + case TCP_NODELAY: + case TCP_KEEPALIVE: +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + case TCP_KEEPINTVL: + case TCP_KEEPCNT: +#endif /* LWIP_TCP_KEEPALIVE */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ +#if LWIP_UDP && LWIP_UDPLITE +/* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + if (*optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no UDP lite socket, ignore any options. */ + if (sock->conn->type != NETCONN_UDPLITE) { + return 0; + } + + switch (optname) { + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP && LWIP_UDPLITE*/ +/* UNDEFINED LEVEL */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + } /* switch */ + + + if (err != ERR_OK) { + sock_set_errno(sock, err); + return -1; + } + + /* Now do the actual option processing */ + data.sock = sock; +#ifdef LWIP_DEBUG + data.s = s; +#endif /* LWIP_DEBUG */ + data.level = level; + data.optname = optname; + data.optval = optval; + data.optlen = optlen; + data.err = err; + tcpip_callback(lwip_getsockopt_internal, &data); + sys_arch_sem_wait(&sock->conn->op_completed, 0); + /* maybe lwip_getsockopt_internal has changed err */ + err = data.err; + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +static void +lwip_getsockopt_internal(void *arg) +{ + struct lwip_sock *sock; +#ifdef LWIP_DEBUG + int s; +#endif /* LWIP_DEBUG */ + int level, optname; + void *optval; + struct lwip_setgetsockopt_data *data; + + LWIP_ASSERT("arg != NULL", arg != NULL); + + data = (struct lwip_setgetsockopt_data*)arg; + sock = data->sock; +#ifdef LWIP_DEBUG + s = data->s; +#endif /* LWIP_DEBUG */ + level = data->level; + optname = data->optname; + optval = data->optval; + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + /* The option flags */ + case SO_ACCEPTCONN: + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case SO_OOBINCLUDE: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /*case SO_USELOOPBACK: UNIMPL */ + *(int*)optval = sock->conn->pcb.ip->so_options & optname; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; + + case SO_TYPE: + switch (NETCONNTYPE_GROUP(sock->conn->type)) { + case NETCONN_RAW: + *(int*)optval = SOCK_RAW; + break; + case NETCONN_TCP: + *(int*)optval = SOCK_STREAM; + break; + case NETCONN_UDP: + *(int*)optval = SOCK_DGRAM; + break; + default: /* unrecognized socket type */ + *(int*)optval = sock->conn->type; + LWIP_DEBUGF(SOCKETS_DEBUG, + ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n", + s, *(int *)optval)); + } /* switch (sock->conn->type) */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n", + s, *(int *)optval)); + break; + + case SO_ERROR: + /* only overwrite ERR_OK or tempoary errors */ + if ((sock->err == 0) || (sock->err == EINPROGRESS)) { + sock_set_errno(sock, err_to_errno(sock->conn->last_err)); + } + *(int *)optval = sock->err; + sock->err = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + *(int *)optval = netconn_get_recvtimeout(sock->conn); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + *(int *)optval = netconn_get_recvbufsize(sock->conn); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0; + break; +#endif /* LWIP_UDP*/ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + *(int*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_TOS: + *(int*)optval = sock->conn->pcb.ip->tos; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n", + s, *(int *)optval)); + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + *(u8_t*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_MULTICAST_IF: + inet_addr_from_ipaddr((struct in_addr*)optval, &sock->conn->pcb.udp->multicast_ip); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n", + s, *(u32_t *)optval)); + break; + case IP_MULTICAST_LOOP: + if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) { + *(u8_t*)optval = 1; + } else { + *(u8_t*)optval = 0; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_IGMP */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + switch (optname) { + case TCP_NODELAY: + *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n", + s, (*(int*)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPINTVL: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPCNT: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + switch (optname) { + case UDPLITE_SEND_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_tx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_rx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + default: + LWIP_ASSERT("unhandled level", 0); + break; + } /* switch (level) */ + sys_sem_signal(&sock->conn->op_completed); +} + +int +lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) +{ + struct lwip_sock *sock = get_socket(s); + err_t err = ERR_OK; + struct lwip_setgetsockopt_data data; + + if (!sock) { + return -1; + } + + if (NULL == optval) { + sock_set_errno(sock, EFAULT); + return -1; + } + + /* Do length and type checks for the various options first, to keep it readable. */ + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case case SO_CONTIMEO: */ + /* UNIMPL case case SO_SNDTIMEO: */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: +#endif /* LWIP_SO_RCVBUF */ + /* UNIMPL case SO_OOBINLINE: */ + /* UNIMPL case SO_SNDBUF: */ + /* UNIMPL case SO_RCVLOWAT: */ + /* UNIMPL case SO_SNDLOWAT: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /* UNIMPL case SO_USELOOPBACK: */ + if (optlen < sizeof(int)) { + err = EINVAL; + } + break; + case SO_NO_CHECK: + if (optlen < sizeof(int)) { + err = EINVAL; + } +#if LWIP_UDP + if ((sock->conn->type != NETCONN_UDP) || + ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { + /* this flag is only available for UDP, not for UDP lite */ + err = EAFNOSUPPORT; + } +#endif /* LWIP_UDP */ + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + /* UNIMPL case IP_HDRINCL: */ + /* UNIMPL case IP_RCVDSTADDR: */ + /* UNIMPL case IP_RCVIF: */ + case IP_TTL: + case IP_TOS: + if (optlen < sizeof(int)) { + err = EINVAL; + } + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + if (optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_MULTICAST_IF: + if (optlen < sizeof(struct in_addr)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_MULTICAST_LOOP: + if (optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + if (optlen < sizeof(struct ip_mreq)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; +#endif /* LWIP_IGMP */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + if (optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no TCP socket, ignore any options. */ + if (sock->conn->type != NETCONN_TCP) + return 0; + + switch (optname) { + case TCP_NODELAY: + case TCP_KEEPALIVE: +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + case TCP_KEEPINTVL: + case TCP_KEEPCNT: +#endif /* LWIP_TCP_KEEPALIVE */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ +#if LWIP_UDP && LWIP_UDPLITE +/* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + if (optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no UDP lite socket, ignore any options. */ + if (sock->conn->type != NETCONN_UDPLITE) + return 0; + + switch (optname) { + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP && LWIP_UDPLITE */ +/* UNDEFINED LEVEL */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + } /* switch (level) */ + + + if (err != ERR_OK) { + sock_set_errno(sock, err); + return -1; + } + + + /* Now do the actual option processing */ + data.sock = sock; +#ifdef LWIP_DEBUG + data.s = s; +#endif /* LWIP_DEBUG */ + data.level = level; + data.optname = optname; + data.optval = (void*)optval; + data.optlen = &optlen; + data.err = err; + tcpip_callback(lwip_setsockopt_internal, &data); + sys_arch_sem_wait(&sock->conn->op_completed, 0); + /* maybe lwip_setsockopt_internal has changed err */ + err = data.err; + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +static void +lwip_setsockopt_internal(void *arg) +{ + struct lwip_sock *sock; +#ifdef LWIP_DEBUG + int s; +#endif /* LWIP_DEBUG */ + int level, optname; + const void *optval; + struct lwip_setgetsockopt_data *data; + + LWIP_ASSERT("arg != NULL", arg != NULL); + + data = (struct lwip_setgetsockopt_data*)arg; + sock = data->sock; +#ifdef LWIP_DEBUG + s = data->s; +#endif /* LWIP_DEBUG */ + level = data->level; + optname = data->optname; + optval = data->optval; + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + /* The option flags */ + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case SO_OOBINCLUDE: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /* UNIMPL case SO_USELOOPBACK: */ + if (*(int*)optval) { + sock->conn->pcb.ip->so_options |= optname; + } else { + sock->conn->pcb.ip->so_options &= ~optname; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + netconn_set_recvtimeout(sock->conn, *(int*)optval); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + netconn_set_recvbufsize(sock->conn, *(int*)optval); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + if (*(int*)optval) { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM); + } else { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM); + } + break; +#endif /* LWIP_UDP */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n", + s, sock->conn->pcb.ip->ttl)); + break; + case IP_TOS: + sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n", + s, sock->conn->pcb.ip->tos)); + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval); + break; + case IP_MULTICAST_IF: + inet_addr_to_ipaddr(&sock->conn->pcb.udp->multicast_ip, (struct in_addr*)optval); + break; + case IP_MULTICAST_LOOP: + if (*(u8_t*)optval) { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP); + } else { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP); + } + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + { + /* If this is a TCP or a RAW socket, ignore these options. */ + struct ip_mreq *imr = (struct ip_mreq *)optval; + ip_addr_t if_addr; + ip_addr_t multi_addr; + inet_addr_to_ipaddr(&if_addr, &imr->imr_interface); + inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr); + if(optname == IP_ADD_MEMBERSHIP){ + data->err = igmp_joingroup(&if_addr, &multi_addr); + } else { + data->err = igmp_leavegroup(&if_addr, &multi_addr); + } + if(data->err != ERR_OK) { + data->err = EADDRNOTAVAIL; + } + } + break; +#endif /* LWIP_IGMP */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + switch (optname) { + case TCP_NODELAY: + if (*(int*)optval) { + tcp_nagle_disable(sock->conn->pcb.tcp); + } else { + tcp_nagle_enable(sock->conn->pcb.tcp); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n", + s, (*(int *)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + case TCP_KEEPINTVL: + sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_intvl)); + break; + case TCP_KEEPCNT: + sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_cnt)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP*/ +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + switch (optname) { + case UDPLITE_SEND_CSCOV: + if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_tx = 8; + } else { + sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_rx = 8; + } else { + sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + default: + LWIP_ASSERT("unhandled level", 0); + break; + } /* switch (level) */ + sys_sem_signal(&sock->conn->op_completed); +} + +int +lwip_ioctl(int s, long cmd, void *argp) +{ + struct lwip_sock *sock = get_socket(s); + u8_t val; +#if LWIP_SO_RCVBUF + u16_t buflen = 0; + s16_t recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + if (!sock) { + return -1; + } + + switch (cmd) { +#if LWIP_SO_RCVBUF + case FIONREAD: + if (!argp) { + sock_set_errno(sock, EINVAL); + return -1; + } + + SYS_ARCH_GET(sock->conn->recv_avail, recv_avail); + if (recv_avail < 0) { + recv_avail = 0; + } + *((u16_t*)argp) = (u16_t)recv_avail; + + /* Check if there is data left from the last recv operation. /maq 041215 */ + if (sock->lastdata) { + struct pbuf *p = (struct pbuf *)sock->lastdata; + if (netconn_type(sock->conn) != NETCONN_TCP) { + p = ((struct netbuf *)p)->p; + } + buflen = p->tot_len; + buflen -= sock->lastoffset; + + *((u16_t*)argp) += buflen; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp))); + sock_set_errno(sock, 0); + return 0; +#endif /* LWIP_SO_RCVBUF */ + + case FIONBIO: + val = 0; + if (argp && *(u32_t*)argp) { + val = 1; + } + netconn_set_nonblocking(sock->conn, val); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val)); + sock_set_errno(sock, 0); + return 0; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp)); + sock_set_errno(sock, ENOSYS); /* not yet implemented */ + return -1; + } /* switch (cmd) */ +} + +/** A minimal implementation of fcntl. + * Currently only the commands F_GETFL and F_SETFL are implemented. + * Only the flag O_NONBLOCK is implemented. + */ +int +lwip_fcntl(int s, int cmd, int val) +{ + struct lwip_sock *sock = get_socket(s); + int ret = -1; + + if (!sock || !sock->conn) { + return -1; + } + + switch (cmd) { + case F_GETFL: + ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0; + break; + case F_SETFL: + if ((val & ~O_NONBLOCK) == 0) { + /* only O_NONBLOCK, all other bits are zero */ + netconn_set_nonblocking(sock->conn, val & O_NONBLOCK); + ret = 0; + } + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val)); + break; + } + return ret; +} + +#endif /* LWIP_SOCKET */ diff --git a/app/lwip/api/tcpip.c b/app/lwip/api/tcpip.c new file mode 100644 index 00000000..01a49d56 --- /dev/null +++ b/app/lwip/api/tcpip.c @@ -0,0 +1,460 @@ +/** + * @file + * Sequential API Main thread module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/memp.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/tcpip.h" +#include "lwip/init.h" +#include "netif/etharp.h" +#include "netif/ppp_oe.h" + +/* global variables */ +static tcpip_init_done_fn tcpip_init_done; +static void *tcpip_init_done_arg; +static sys_mbox_t mbox; + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +sys_mutex_t lock_tcpip_core; +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + +/** + * The main lwIP thread. This thread has exclusive access to lwIP core functions + * (unless access to them is not locked). Other threads communicate with this + * thread using message boxes. + * + * It also starts all the timers to make sure they are running in the right + * thread context. + * + * @param arg unused argument + */ +static void +tcpip_thread(void *arg) +{ + struct tcpip_msg *msg; + LWIP_UNUSED_ARG(arg); + + if (tcpip_init_done != NULL) {//ûעԶʼ + tcpip_init_done(tcpip_init_done_arg); + } + + LOCK_TCPIP_CORE(); + while (1) { /* MAIN Loop */ + UNLOCK_TCPIP_CORE(); + LWIP_TCPIP_THREAD_ALIVE(); + /* wait for a message, timeouts are processed while waiting */ + sys_timeouts_mbox_fetch(&mbox, (void **)&msg); + LOCK_TCPIP_CORE(); + switch (msg->type) { +#if LWIP_NETCONN + case TCPIP_MSG_API://API + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg)); + msg->msg.apimsg->function(&(msg->msg.apimsg->msg)); + break; +#endif /* LWIP_NETCONN */ + +#if !LWIP_TCPIP_CORE_LOCKING_INPUT + case TCPIP_MSG_INPKT://ײݰ + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); +#if LWIP_ETHERNET + if (msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {//֧ARP + ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);//ARP + } else +#endif /* LWIP_ETHERNET */ + { + ip_input(msg->msg.inp.p, msg->msg.inp.netif);//IP + } + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + break; +#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */ + +#if LWIP_NETIF_API + case TCPIP_MSG_NETIFAPI: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg)); + msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg)); + break; +#endif /* LWIP_NETIF_API */ + + case TCPIP_MSG_CALLBACK://ϲصʽִһ + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + +#if LWIP_TCPIP_TIMEOUT + case TCPIP_MSG_TIMEOUT://ϲעһʱ¼ + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg)); + sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + case TCPIP_MSG_UNTIMEOUT://ϲɾһʱ¼ + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg)); + sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; +#endif /* LWIP_TCPIP_TIMEOUT */ + + default: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type)); + LWIP_ASSERT("tcpip_thread: invalid message", 0); + break; + } + } +} + +/** + * Pass a received packet to tcpip_thread for input processing + * + * @param p the received packet, p->payload pointing to the Ethernet header or + * to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or + * NETIF_FLAG_ETHERNET flags) + * @param inp the network interface on which the packet was received + */ +err_t +tcpip_input(struct pbuf *p, struct netif *inp) +{ +#if LWIP_TCPIP_CORE_LOCKING_INPUT + err_t ret; + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_input: PACKET %p/%p\n", (void *)p, (void *)inp)); + LOCK_TCPIP_CORE(); +#if LWIP_ETHERNET + if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) { + ret = ethernet_input(p, inp); + } else +#endif /* LWIP_ETHERNET */ + { + ret = ip_input(p, inp); + } + UNLOCK_TCPIP_CORE(); + return ret; +#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_INPKT; + msg->msg.inp.p = p; + msg->msg.inp.netif = inp; + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + return ERR_MEM; + } + return ERR_OK; + } + return ERR_VAL; +#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */ +} + +/** + * Call a specific function in the thread context of + * tcpip_thread for easy access synchronization. + * A function called in that way may access lwIP core code + * without fearing concurrent access. + * + * @param f the function to call + * @param ctx parameter passed to f + * @param block 1 to block until the request is posted, 0 to non-blocking mode + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block) +{ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_CALLBACK; + msg->msg.cb.function = function; + msg->msg.cb.ctx = ctx; + if (block) { + sys_mbox_post(&mbox, msg); + } else { + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_API, msg); + return ERR_MEM; + } + } + return ERR_OK; + } + return ERR_VAL; +} + +#if LWIP_TCPIP_TIMEOUT +/** + * call sys_timeout in tcpip_thread + * + * @param msec time in milliseconds for timeout + * @param h function to be called on timeout + * @param arg argument to pass to timeout function h + * @return ERR_MEM on memory error, ERR_OK otherwise + */ +err_t +tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_TIMEOUT; + msg->msg.tmo.msecs = msecs; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(&mbox, msg); + return ERR_OK; + } + return ERR_VAL; +} + +/** + * call sys_untimeout in tcpip_thread + * + * @param msec time in milliseconds for timeout + * @param h function to be called on timeout + * @param arg argument to pass to timeout function h + * @return ERR_MEM on memory error, ERR_OK otherwise + */ +err_t +tcpip_untimeout(sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_UNTIMEOUT; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(&mbox, msg); + return ERR_OK; + } + return ERR_VAL; +} +#endif /* LWIP_TCPIP_TIMEOUT */ + +#if LWIP_NETCONN +/** + * Call the lower part of a netconn_* function + * This function is then running in the thread context + * of tcpip_thread and has exclusive access to lwIP core code. + * + * @param apimsg a struct containing the function to call and its parameters + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_apimsg(struct api_msg *apimsg) +{ + struct tcpip_msg msg; +#ifdef LWIP_DEBUG + /* catch functions that don't set err */ + apimsg->msg.err = ERR_VAL; +#endif + + if (sys_mbox_valid(&mbox)) {//ںЧ + msg.type = TCPIP_MSG_API; + msg.msg.apimsg = apimsg; + sys_mbox_post(&mbox, &msg);//ͶϢ + sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0);//ȴϢ + return apimsg->msg.err; + } + return ERR_VAL; +} + +#if LWIP_TCPIP_CORE_LOCKING +/** + * Call the lower part of a netconn_* function + * This function has exclusive access to lwIP core code by locking it + * before the function is called. + * + * @param apimsg a struct containing the function to call and its parameters + * @return ERR_OK (only for compatibility fo tcpip_apimsg()) + */ +err_t +tcpip_apimsg_lock(struct api_msg *apimsg) +{ +#ifdef LWIP_DEBUG + /* catch functions that don't set err */ + apimsg->msg.err = ERR_VAL; +#endif + + LOCK_TCPIP_CORE(); + apimsg->function(&(apimsg->msg)); + UNLOCK_TCPIP_CORE(); + return apimsg->msg.err; + +} +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETCONN */ + +#if LWIP_NETIF_API +#if !LWIP_TCPIP_CORE_LOCKING +/** + * Much like tcpip_apimsg, but calls the lower part of a netifapi_* + * function. + * + * @param netifapimsg a struct containing the function to call and its parameters + * @return error code given back by the function that was called + */ +err_t +tcpip_netifapi(struct netifapi_msg* netifapimsg) +{ + struct tcpip_msg msg; + + if (sys_mbox_valid(&mbox)) { + err_t err = sys_sem_new(&netifapimsg->msg.sem, 0); + if (err != ERR_OK) { + netifapimsg->msg.err = err; + return err; + } + + msg.type = TCPIP_MSG_NETIFAPI; + msg.msg.netifapimsg = netifapimsg; + sys_mbox_post(&mbox, &msg); + sys_sem_wait(&netifapimsg->msg.sem); + sys_sem_free(&netifapimsg->msg.sem); + return netifapimsg->msg.err; + } + return ERR_VAL; +} +#else /* !LWIP_TCPIP_CORE_LOCKING */ +/** + * Call the lower part of a netifapi_* function + * This function has exclusive access to lwIP core code by locking it + * before the function is called. + * + * @param netifapimsg a struct containing the function to call and its parameters + * @return ERR_OK (only for compatibility fo tcpip_netifapi()) + */ +err_t +tcpip_netifapi_lock(struct netifapi_msg* netifapimsg) +{ + LOCK_TCPIP_CORE(); + netifapimsg->function(&(netifapimsg->msg)); + UNLOCK_TCPIP_CORE(); + return netifapimsg->msg.err; +} +#endif /* !LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETIF_API */ + +/** + * Initialize this module: + * - initialize all sub modules + * - start the tcpip_thread + * + * @param initfunc a function to call when tcpip_thread is running and finished initializing + * @param arg argument to pass to initfunc + */ +void +tcpip_init(tcpip_init_done_fn initfunc, void *arg) +{ + lwip_init();//ʼں + + tcpip_init_done = initfunc;//עûԶ庯 + tcpip_init_done_arg = arg;// + if(sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {//ں + LWIP_ASSERT("failed to create tcpip_thread mbox", 0); + } +#if LWIP_TCPIP_CORE_LOCKING + if(sys_mutex_new(&lock_tcpip_core) != ERR_OK) { + LWIP_ASSERT("failed to create lock_tcpip_core", 0); + } +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);//ں˽ +} + +/** + * Simple callback function used with tcpip_callback to free a pbuf + * (pbuf_free has a wrong signature for tcpip_callback) + * + * @param p The pbuf (chain) to be dereferenced. + */ +static void +pbuf_free_int(void *p) +{ + struct pbuf *q = (struct pbuf *)p; + pbuf_free(q); +} + +/** + * A simple wrapper function that allows you to free a pbuf from interrupt context. + * + * @param p The pbuf (chain) to be dereferenced. + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +pbuf_free_callback(struct pbuf *p) +{ + return tcpip_callback_with_block(pbuf_free_int, p, 0); +} + +/** + * A simple wrapper function that allows you to free heap memory from + * interrupt context. + * + * @param m the heap memory to free + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +mem_free_callback(void *m) +{ + return tcpip_callback_with_block(mem_free, m, 0); +} + +#endif /* !NO_SYS */ diff --git a/app/lwip/app/Makefile b/app/lwip/app/Makefile new file mode 100644 index 00000000..3c2954ef --- /dev/null +++ b/app/lwip/app/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = liblwipapp.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/lwip/app/dhcpserver.c b/app/lwip/app/dhcpserver.c new file mode 100644 index 00000000..81b9a3d4 --- /dev/null +++ b/app/lwip/app/dhcpserver.c @@ -0,0 +1,858 @@ +#include "lwip/inet.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/udp.h" +#include "lwip/mem.h" +//#include "crypto/common.h" +#include "osapi.h" +#include "lwip/app/dhcpserver.h" + +#ifndef LWIP_OPEN_SRC +#include "net80211/ieee80211_var.h" +#endif +#include "user_interface.h" + +//////////////////////////////////////////////////////////////////////////////////// +static const uint8_t xid[4] = {0xad, 0xde, 0x12, 0x23}; +static u8_t old_xid[4] = {0}; +static const uint8_t magic_cookie[4] = {99, 130, 83, 99}; +static struct udp_pcb *pcb_dhcps = NULL; +static struct ip_addr broadcast_dhcps; +static struct ip_addr server_address; +static struct ip_addr client_address;//added +static struct ip_addr client_address_plus; +static struct dhcps_msg msg_dhcps; +struct dhcps_state s; + +static struct dhcps_lease dhcps_lease; +static bool dhcps_lease_flag = true; +static list_node *plist = NULL; +/****************************************************************************** + * FunctionName : node_insert_to_list + * Description : insert the node to the list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR node_insert_to_list(list_node **phead, list_node* pinsert) +{ + list_node *plist = NULL; + + if (*phead == NULL) + *phead = pinsert; + else { + plist = *phead; + while (plist->pnext != NULL) { + plist = plist->pnext; + } + plist->pnext = pinsert; + } + pinsert->pnext = NULL; +} + +/****************************************************************************** + * FunctionName : node_delete_from_list + * Description : remove the node from list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR node_remove_from_list(list_node **phead, list_node* pdelete) +{ + list_node *plist = NULL; + + plist = *phead; + if (plist == NULL){ + *phead = NULL; + } else { + if (plist == pdelete){ + *phead = plist->pnext; + } else { + while (plist != NULL) { + if (plist->pnext == pdelete){ + plist->pnext = pdelete->pnext; + } + plist = plist->pnext; + } + } + } +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ��DHCP msg��Ϣ�ṹ���������� + * + * @param optptr -- DHCP msg��Ϣλ�� + * @param type -- Ҫ��ӵ�����option + * + * @return uint8_t* ����DHCP msgƫ�Ƶ�ַ + */ +/////////////////////////////////////////////////////////////////////////////////// +static uint8_t* ICACHE_FLASH_ATTR add_msg_type(uint8_t *optptr, uint8_t type) +{ + + *optptr++ = DHCP_OPTION_MSG_TYPE; + *optptr++ = 1; + *optptr++ = type; + return optptr; +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ��DHCP msg�ṹ������offerӦ������ + * + * @param optptr -- DHCP msg��Ϣλ�� + * + * @return uint8_t* ����DHCP msgƫ�Ƶ�ַ + */ +/////////////////////////////////////////////////////////////////////////////////// +static uint8_t* ICACHE_FLASH_ATTR add_offer_options(uint8_t *optptr) +{ + struct ip_addr ipadd; + + ipadd.addr = *( (uint32_t *) &server_address); + +#ifdef USE_CLASS_B_NET + *optptr++ = DHCP_OPTION_SUBNET_MASK; + *optptr++ = 4; //length + *optptr++ = 255; + *optptr++ = 240; + *optptr++ = 0; + *optptr++ = 0; +#else + *optptr++ = DHCP_OPTION_SUBNET_MASK; + *optptr++ = 4; + *optptr++ = 255; + *optptr++ = 255; + *optptr++ = 255; + *optptr++ = 0; +#endif + + *optptr++ = DHCP_OPTION_LEASE_TIME; + *optptr++ = 4; + *optptr++ = 0x00; + *optptr++ = 0x01; + *optptr++ = 0x51; + *optptr++ = 0x80; + + *optptr++ = DHCP_OPTION_SERVER_ID; + *optptr++ = 4; + *optptr++ = ip4_addr1( &ipadd); + *optptr++ = ip4_addr2( &ipadd); + *optptr++ = ip4_addr3( &ipadd); + *optptr++ = ip4_addr4( &ipadd); + + *optptr++ = DHCP_OPTION_ROUTER; + *optptr++ = 4; + *optptr++ = ip4_addr1( &ipadd); + *optptr++ = ip4_addr2( &ipadd); + *optptr++ = ip4_addr3( &ipadd); + *optptr++ = ip4_addr4( &ipadd); + +#ifdef USE_DNS + *optptr++ = DHCP_OPTION_DNS_SERVER; + *optptr++ = 4; + *optptr++ = ip4_addr1( &ipadd); + *optptr++ = ip4_addr2( &ipadd); + *optptr++ = ip4_addr3( &ipadd); + *optptr++ = ip4_addr4( &ipadd); +#endif + +#ifdef CLASS_B_NET + *optptr++ = DHCP_OPTION_BROADCAST_ADDRESS; + *optptr++ = 4; + *optptr++ = ip4_addr1( &ipadd); + *optptr++ = 255; + *optptr++ = 255; + *optptr++ = 255; +#else + *optptr++ = DHCP_OPTION_BROADCAST_ADDRESS; + *optptr++ = 4; + *optptr++ = ip4_addr1( &ipadd); + *optptr++ = ip4_addr2( &ipadd); + *optptr++ = ip4_addr3( &ipadd); + *optptr++ = 255; +#endif + + *optptr++ = DHCP_OPTION_INTERFACE_MTU; + *optptr++ = 2; +#ifdef CLASS_B_NET + *optptr++ = 0x05; + *optptr++ = 0xdc; +#else + *optptr++ = 0x02; + *optptr++ = 0x40; +#endif + + *optptr++ = DHCP_OPTION_PERFORM_ROUTER_DISCOVERY; + *optptr++ = 1; + *optptr++ = 0x00; + + *optptr++ = 43; + *optptr++ = 6; + + *optptr++ = 0x01; + *optptr++ = 4; + *optptr++ = 0x00; + *optptr++ = 0x00; + *optptr++ = 0x00; + *optptr++ = 0x02; + + return optptr; +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ��DHCP msg�ṹ����ӽ����־���� + * + * @param optptr -- DHCP msg��Ϣλ�� + * + * @return uint8_t* ����DHCP msgƫ�Ƶ�ַ + */ +/////////////////////////////////////////////////////////////////////////////////// +static uint8_t* ICACHE_FLASH_ATTR add_end(uint8_t *optptr) +{ + + *optptr++ = DHCP_OPTION_END; + return optptr; +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ����һ��DHCP msg�ṹ�� + * + * @param -- m ָ�򴴽���DHCP msg�ṹ�����? + */ +/////////////////////////////////////////////////////////////////////////////////// +static void ICACHE_FLASH_ATTR create_msg(struct dhcps_msg *m) +{ + struct ip_addr client; + + client.addr = *( (uint32_t *) &client_address); + + m->op = DHCP_REPLY; + m->htype = DHCP_HTYPE_ETHERNET; + m->hlen = 6; + m->hops = 0; + os_memcpy((char *) xid, (char *) m->xid, sizeof(m->xid)); + m->secs = 0; + m->flags = htons(BOOTP_BROADCAST); + + os_memcpy((char *) m->yiaddr, (char *) &client.addr, sizeof(m->yiaddr)); + + os_memset((char *) m->ciaddr, 0, sizeof(m->ciaddr)); + os_memset((char *) m->siaddr, 0, sizeof(m->siaddr)); + os_memset((char *) m->giaddr, 0, sizeof(m->giaddr)); + os_memset((char *) m->sname, 0, sizeof(m->sname)); + os_memset((char *) m->file, 0, sizeof(m->file)); + + os_memset((char *) m->options, 0, sizeof(m->options)); + os_memcpy((char *) m->options, (char *) magic_cookie, sizeof(magic_cookie)); +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ����һ��OFFER + * + * @param -- m ָ����Ҫ���͵�DHCP msg���� + */ +/////////////////////////////////////////////////////////////////////////////////// +static void ICACHE_FLASH_ATTR send_offer(struct dhcps_msg *m) +{ + uint8_t *end; + struct pbuf *p, *q; + u8_t *data; + u16_t cnt=0; + u16_t i; + err_t SendOffer_err_t; + create_msg(m); + + end = add_msg_type(&m->options[4], DHCPOFFER); + end = add_offer_options(end); + end = add_end(end); + + p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); +#if DHCPS_DEBUG + os_printf("udhcp: send_offer>>p->ref = %d\n", p->ref); +#endif + if(p != NULL){ + +#if DHCPS_DEBUG + os_printf("dhcps: send_offer>>pbuf_alloc succeed\n"); + os_printf("dhcps: send_offer>>p->tot_len = %d\n", p->tot_len); + os_printf("dhcps: send_offer>>p->len = %d\n", p->len); +#endif + q = p; + while(q != NULL){ + data = (u8_t *)q->payload; + for(i=0; ilen; i++) + { + data[i] = ((u8_t *) m)[cnt++]; +#if DHCPS_DEBUG + os_printf("%02x ",data[i]); + if((i+1)%16 == 0){ + os_printf("\n"); + } +#endif + } + + q = q->next; + } + }else{ + +#if DHCPS_DEBUG + os_printf("dhcps: send_offer>>pbuf_alloc failed\n"); +#endif + return; + } + SendOffer_err_t = udp_sendto( pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT ); +#if DHCPS_DEBUG + os_printf("dhcps: send_offer>>udp_sendto result %x\n",SendOffer_err_t); +#endif + if(p->ref != 0){ +#if DHCPS_DEBUG + os_printf("udhcp: send_offer>>free pbuf\n"); +#endif + pbuf_free(p); + } +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ����һ��NAK��Ϣ + * + * @param m ָ����Ҫ���͵�DHCP msg���� + */ +/////////////////////////////////////////////////////////////////////////////////// +static void ICACHE_FLASH_ATTR send_nak(struct dhcps_msg *m) +{ + + u8_t *end; + struct pbuf *p, *q; + u8_t *data; + u16_t cnt=0; + u16_t i; + err_t SendNak_err_t; + create_msg(m); + + end = add_msg_type(&m->options[4], DHCPNAK); + end = add_end(end); + + p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); +#if DHCPS_DEBUG + os_printf("udhcp: send_nak>>p->ref = %d\n", p->ref); +#endif + if(p != NULL){ + +#if DHCPS_DEBUG + os_printf("dhcps: send_nak>>pbuf_alloc succeed\n"); + os_printf("dhcps: send_nak>>p->tot_len = %d\n", p->tot_len); + os_printf("dhcps: send_nak>>p->len = %d\n", p->len); +#endif + q = p; + while(q != NULL){ + data = (u8_t *)q->payload; + for(i=0; ilen; i++) + { + data[i] = ((u8_t *) m)[cnt++]; +#if DHCPS_DEBUG + os_printf("%02x ",data[i]); + if((i+1)%16 == 0){ + os_printf("\n"); + } +#endif + } + + q = q->next; + } + }else{ + +#if DHCPS_DEBUG + os_printf("dhcps: send_nak>>pbuf_alloc failed\n"); +#endif + return; + } + SendNak_err_t = udp_sendto( pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT ); +#if DHCPS_DEBUG + os_printf("dhcps: send_nak>>udp_sendto result %x\n",SendNak_err_t); +#endif + if(p->ref != 0){ +#if DHCPS_DEBUG + os_printf("udhcp: send_nak>>free pbuf\n"); +#endif + pbuf_free(p); + } +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ����һ��ACK��DHCP�ͻ��� + * + * @param m ָ����Ҫ���͵�DHCP msg���� + */ +/////////////////////////////////////////////////////////////////////////////////// +static void ICACHE_FLASH_ATTR send_ack(struct dhcps_msg *m) +{ + + u8_t *end; + struct pbuf *p, *q; + u8_t *data; + u16_t cnt=0; + u16_t i; + err_t SendAck_err_t; + create_msg(m); + + end = add_msg_type(&m->options[4], DHCPACK); + end = add_offer_options(end); + end = add_end(end); + + p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); +#if DHCPS_DEBUG + os_printf("udhcp: send_ack>>p->ref = %d\n", p->ref); +#endif + if(p != NULL){ + +#if DHCPS_DEBUG + os_printf("dhcps: send_ack>>pbuf_alloc succeed\n"); + os_printf("dhcps: send_ack>>p->tot_len = %d\n", p->tot_len); + os_printf("dhcps: send_ack>>p->len = %d\n", p->len); +#endif + q = p; + while(q != NULL){ + data = (u8_t *)q->payload; + for(i=0; ilen; i++) + { + data[i] = ((u8_t *) m)[cnt++]; +#if DHCPS_DEBUG + os_printf("%02x ",data[i]); + if((i+1)%16 == 0){ + os_printf("\n"); + } +#endif + } + + q = q->next; + } + }else{ + +#if DHCPS_DEBUG + os_printf("dhcps: send_ack>>pbuf_alloc failed\n"); +#endif + return; + } + SendAck_err_t = udp_sendto( pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT ); +#if DHCPS_DEBUG + os_printf("dhcps: send_ack>>udp_sendto result %x\n",SendAck_err_t); +#endif + + if(p->ref != 0){ +#if DHCPS_DEBUG + os_printf("udhcp: send_ack>>free pbuf\n"); +#endif + pbuf_free(p); + } +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ����DHCP�ͻ��˷�����DHCP����������Ϣ�����Բ�ͬ��DHCP��������������Ӧ��Ӧ�� + * + * @param optptr DHCP msg�е��������� + * @param len ��������Ĵ��?(byte) + * + * @return uint8_t ���ش�����DHCP Server״ֵ̬ + */ +/////////////////////////////////////////////////////////////////////////////////// +static uint8_t ICACHE_FLASH_ATTR parse_options(uint8_t *optptr, sint16_t len) +{ + struct ip_addr client; + bool is_dhcp_parse_end = false; + + client.addr = *( (uint32_t *) &client_address);// Ҫ�����DHCP�ͻ��˵�IP + + u8_t *end = optptr + len; + u16_t type = 0; + + s.state = DHCPS_STATE_IDLE; + + while (optptr < end) { +#if DHCPS_DEBUG + os_printf("dhcps: (sint16_t)*optptr = %d\n", (sint16_t)*optptr); +#endif + switch ((sint16_t) *optptr) { + + case DHCP_OPTION_MSG_TYPE: //53 + type = *(optptr + 2); + break; + + case DHCP_OPTION_REQ_IPADDR://50 + if( os_memcmp( (char *) &client.addr, (char *) optptr+2,4)==0 ) { +#if DHCPS_DEBUG + os_printf("dhcps: DHCP_OPTION_REQ_IPADDR = 0 ok\n"); +#endif + s.state = DHCPS_STATE_ACK; + }else { +#if DHCPS_DEBUG + os_printf("dhcps: DHCP_OPTION_REQ_IPADDR != 0 err\n"); +#endif + s.state = DHCPS_STATE_NAK; + } + break; + case DHCP_OPTION_END: + { + is_dhcp_parse_end = true; + } + break; + } + + if(is_dhcp_parse_end){ + break; + } + + optptr += optptr[1] + 2; + } + + switch (type){ + + case DHCPDISCOVER://1 + s.state = DHCPS_STATE_OFFER; +#if DHCPS_DEBUG + os_printf("dhcps: DHCPD_STATE_OFFER\n"); +#endif + break; + + case DHCPREQUEST://3 + if ( !(s.state == DHCPS_STATE_ACK || s.state == DHCPS_STATE_NAK) ) { + s.state = DHCPS_STATE_NAK; +#if DHCPS_DEBUG + os_printf("dhcps: DHCPD_STATE_NAK\n"); +#endif + } + break; + + case DHCPDECLINE://4 + s.state = DHCPS_STATE_IDLE; +#if DHCPS_DEBUG + os_printf("dhcps: DHCPD_STATE_IDLE\n"); +#endif + break; + + case DHCPRELEASE://7 + s.state = DHCPS_STATE_IDLE; +#if DHCPS_DEBUG + os_printf("dhcps: DHCPD_STATE_IDLE\n"); +#endif + break; + } +#if DHCPS_DEBUG + os_printf("dhcps: return s.state = %d\n", s.state); +#endif + return s.state; +} +/////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////// +static sint16_t ICACHE_FLASH_ATTR parse_msg(struct dhcps_msg *m, u16_t len) +{ + if(os_memcmp((char *)m->options, + (char *)magic_cookie, + sizeof(magic_cookie)) == 0){ +#if DHCPS_DEBUG + os_printf("dhcps: len = %d\n", len); +#endif + /* + * ��¼��ǰ��xid���ﴦ���? + * �˺�ΪDHCP�ͻ����������û�ͳһ��ȡIPʱ�� + */ +// if((old_xid[0] == 0) && +// (old_xid[1] == 0) && +// (old_xid[2] == 0) && +// (old_xid[3] == 0)){ +// /* +// * old_xidδ��¼�κ����? +// * �϶��ǵ�һ��ʹ�� +// */ +// os_memcpy((char *)old_xid, (char *)m->xid, sizeof(m->xid)); +// }else{ +// /* +// * ���δ����DHCP msg��Я���xid���ϴμ�¼�IJ�ͬ�� +// * �϶�Ϊ��ͬ��DHCP�ͻ��˷��ͣ���ʱ����Ҫ����Ŀͻ���IP +// * ���� 192.168.4.100(0x6404A8C0) <--> 192.168.4.200(0xC804A8C0) +// * +// */ +// if(os_memcmp((char *)old_xid, (char *)m->xid, sizeof(m->xid)) != 0){ + /* + * ��¼���ε�xid�ţ�ͬʱ�����IP���� + */ + struct ip_addr addr_tmp; + os_memcpy((char *)old_xid, (char *)m->xid, sizeof(m->xid)); + + { + struct dhcps_pool *pdhcps_pool = NULL; + list_node *pnode = NULL; + list_node *pback_node = NULL; + + POOL_START: + client_address.addr = client_address_plus.addr; +// addr_tmp.addr = htonl(client_address_plus.addr); +// addr_tmp.addr++; +// client_address_plus.addr = htonl(addr_tmp.addr); + for (pback_node = plist; pback_node != NULL;pback_node = pback_node->pnext) { + pdhcps_pool = pback_node->pnode; + if (os_memcmp(pdhcps_pool->mac, m->chaddr, sizeof(pdhcps_pool->mac)) == 0){ +// os_printf("the same device request ip\n"); + client_address.addr = pdhcps_pool->ip.addr; + pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER; + goto POOL_CHECK; + } else if (pdhcps_pool->ip.addr == client_address_plus.addr){ +// client_address.addr = client_address_plus.addr; +// os_printf("the ip addr has been request\n"); + addr_tmp.addr = htonl(client_address_plus.addr); + addr_tmp.addr++; + client_address_plus.addr = htonl(addr_tmp.addr); + client_address.addr = client_address_plus.addr; + } + } + pdhcps_pool = (struct dhcps_pool *)os_zalloc(sizeof(struct dhcps_pool)); + pdhcps_pool->ip.addr = client_address.addr; + os_memcpy(pdhcps_pool->mac, m->chaddr, sizeof(pdhcps_pool->mac)); + pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER; + pnode = (list_node *)os_zalloc(sizeof(list_node )); + pnode->pnode = pdhcps_pool; + node_insert_to_list(&plist,pnode); + + POOL_CHECK: + if ((client_address_plus.addr > dhcps_lease.end_ip) || (ip_addr_isany(&client_address))){ + client_address_plus.addr = dhcps_lease.start_ip; + goto POOL_START; + } + + if (wifi_softap_set_station_info(m->chaddr, &client_address) == false) { + return 0; + } + } + +#if DHCPS_DEBUG + os_printf("dhcps: xid changed\n"); + os_printf("dhcps: client_address.addr = %x\n", client_address.addr); +#endif + +// } + +// } + + return parse_options(&m->options[4], len); + } + return 0; +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * DHCP ��������ݰ���մ���ص�����˺�����LWIP UDPģ������ʱ������ + * ��Ҫ����udp_recv()������LWIP����ע��. + * + * @param arg + * @param pcb ���յ�UDP��Ŀ��ƿ�? + * @param p ���յ���UDP�е��������? + * @param addr ���ʹ�UDP���Դ�����IP��ַ + * @param port ���ʹ�UDP���Դ�����UDPͨ���˿ں� + */ +/////////////////////////////////////////////////////////////////////////////////// +static void ICACHE_FLASH_ATTR handle_dhcp(void *arg, + struct udp_pcb *pcb, + struct pbuf *p, + struct ip_addr *addr, + uint16_t port) +{ + + sint16_t tlen; + u16_t i; + u16_t dhcps_msg_cnt=0; + u8_t *p_dhcps_msg = (u8_t *)&msg_dhcps; + u8_t *data; + +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> receive a packet\n"); +#endif + if (p==NULL) return; + + tlen = p->tot_len; + data = p->payload; + +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> p->tot_len = %d\n", tlen); + os_printf("dhcps: handle_dhcp-> p->len = %d\n", p->len); +#endif + + os_memset(&msg_dhcps, 0, sizeof(dhcps_msg)); + for(i=0; ilen; i++){ + p_dhcps_msg[dhcps_msg_cnt++] = data[i]; +#if DHCPS_DEBUG + os_printf("%02x ",data[i]); + if((i+1)%16 == 0){ + os_printf("\n"); + } +#endif + } + + if(p->next != NULL) { +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> p->next != NULL\n"); + os_printf("dhcps: handle_dhcp-> p->next->tot_len = %d\n",p->next->tot_len); + os_printf("dhcps: handle_dhcp-> p->next->len = %d\n",p->next->len); +#endif + + data = p->next->payload; + for(i=0; inext->len; i++){ + p_dhcps_msg[dhcps_msg_cnt++] = data[i]; +#if DHCPS_DEBUG + os_printf("%02x ",data[i]); + if((i+1)%16 == 0){ + os_printf("\n"); + } +#endif + } + } + + /* + * DHCP �ͻ���������Ϣ���� + */ +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> parse_msg(p)\n"); +#endif + + switch(parse_msg(&msg_dhcps, tlen - 240)) { + + case DHCPS_STATE_OFFER://1 +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> DHCPD_STATE_OFFER\n"); +#endif + send_offer(&msg_dhcps); + break; + case DHCPS_STATE_ACK://3 +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> DHCPD_STATE_ACK\n"); +#endif + send_ack(&msg_dhcps); + break; + case DHCPS_STATE_NAK://4 +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> DHCPD_STATE_NAK\n"); +#endif + send_nak(&msg_dhcps); + break; + default : + break; + } +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> pbuf_free(p)\n"); +#endif + pbuf_free(p); +} +/////////////////////////////////////////////////////////////////////////////////// +static void ICACHE_FLASH_ATTR wifi_softap_init_dhcps_lease(uint32 ip) +{ + uint32 softap_ip = 0,local_ip = 0; + + if (dhcps_lease_flag) { + local_ip = softap_ip = htonl(ip); + softap_ip &= 0xFFFFFF00; + local_ip &= 0xFF; + if (local_ip >= 0x80) + local_ip -= DHCPS_MAX_LEASE; + else + local_ip ++; + + os_bzero(&dhcps_lease, sizeof(dhcps_lease)); + dhcps_lease.start_ip = softap_ip | local_ip; + dhcps_lease.end_ip = softap_ip | (local_ip + DHCPS_MAX_LEASE); + } + dhcps_lease.start_ip = htonl(dhcps_lease.start_ip); + dhcps_lease.end_ip= htonl(dhcps_lease.end_ip); +// os_printf("start_ip = 0x%x, end_ip = 0x%x\n",dhcps_lease.start_ip, dhcps_lease.end_ip); +} +/////////////////////////////////////////////////////////////////////////////////// +void ICACHE_FLASH_ATTR dhcps_start(struct ip_info *info) +{ + os_memset(&msg_dhcps, 0, sizeof(dhcps_msg)); + pcb_dhcps = udp_new(); + if (pcb_dhcps == NULL || info ==NULL) { + os_printf("dhcps_start(): could not obtain pcb\n"); + } + + IP4_ADDR(&broadcast_dhcps, 255, 255, 255, 255); + + server_address = info->ip; + wifi_softap_init_dhcps_lease(server_address.addr); + client_address_plus.addr = dhcps_lease.start_ip; + + udp_bind(pcb_dhcps, IP_ADDR_ANY, DHCPS_SERVER_PORT); + udp_recv(pcb_dhcps, handle_dhcp, NULL); +#if DHCPS_DEBUG + os_printf("dhcps:dhcps_start->udp_recv function Set a receive callback handle_dhcp for UDP_PCB pcb_dhcps\n"); +#endif + +} + +void ICACHE_FLASH_ATTR dhcps_stop(void) +{ + udp_disconnect(pcb_dhcps); + udp_remove(pcb_dhcps); + list_node *pnode = NULL; + list_node *pback_node = NULL; + pnode = plist; + while (pnode != NULL) { + pback_node = pnode; + pnode = pback_node->pnext; + node_remove_from_list(&plist, pback_node); + os_free(pback_node->pnode); + pback_node->pnode = NULL; + os_free(pback_node); + pback_node = NULL; + } +} + +bool ICACHE_FLASH_ATTR wifi_softap_set_dhcps_lease(struct dhcps_lease *please) +{ + struct ip_info info; + uint32 softap_ip = 0; + if (please == NULL) + return false; + + os_bzero(&info, sizeof(struct ip_info)); + wifi_get_ip_info(SOFTAP_IF, &info); + softap_ip = htonl(info.ip.addr); + please->start_ip = htonl(please->start_ip); + please->end_ip = htonl(please->end_ip); + + /*config ip information can't contain local ip*/ + if ((please->start_ip <= softap_ip) && (softap_ip <= please->end_ip)) + return false; + + /*config ip information must be in the same segment as the local ip*/ + softap_ip >>= 8; + if ((please->start_ip >> 8 != softap_ip) + || (please->end_ip >> 8 != softap_ip)) { + return false; + } + + if (please->end_ip - please->start_ip > DHCPS_MAX_LEASE) + return false; + + os_bzero(&dhcps_lease, sizeof(dhcps_lease)); + dhcps_lease.start_ip = please->start_ip; + dhcps_lease.end_ip = please->end_ip; + dhcps_lease_flag = false; + return true; +} + +void ICACHE_FLASH_ATTR dhcps_coarse_tmr(void) +{ + list_node *pback_node = NULL; + list_node *pnode = NULL; + struct dhcps_pool *pdhcps_pool = NULL; + pnode = plist; + while (pnode != NULL) { + pdhcps_pool = pnode->pnode; + pdhcps_pool->lease_timer --; + if (pdhcps_pool->lease_timer == 0){ + pback_node = pnode; + pnode = pback_node->pnext; + node_remove_from_list(&plist,pback_node); + os_free(pback_node->pnode); + pback_node->pnode = NULL; + os_free(pback_node); + pback_node = NULL; + } else { + pnode = pnode ->pnext; + } + } +} diff --git a/app/lwip/app/espconn.c b/app/lwip/app/espconn.c new file mode 100644 index 00000000..15dcc2dd --- /dev/null +++ b/app/lwip/app/espconn.c @@ -0,0 +1,719 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: espconn.c + * + * Description: espconn interface for user + * + * Modification history: + * 2014/3/31, v1.0 create this file. +*******************************************************************************/ + +#include "lwip/netif.h" +#include "lwip/inet.h" +#include "netif/etharp.h" +#include "lwip/tcp.h" +#include "lwip/ip.h" +#include "lwip/init.h" +#include "ets_sys.h" +#include "os_type.h" +//#include "os.h" +#include "lwip/mem.h" + +#include "lwip/app/espconn_tcp.h" +#include "lwip/app/espconn_udp.h" +#include "lwip/app/espconn.h" + +#include "user_interface.h" + +espconn_msg *plink_active = NULL; +espconn_msg *pserver_list = NULL; +remot_info premot[5]; +uint32 link_timer = 0; + +/****************************************************************************** + * FunctionName : espconn_copy_partial + * Description : reconnect with host + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_copy_partial(struct espconn *pesp_dest, struct espconn *pesp_source) +{ + pesp_dest->type = pesp_source->type; + pesp_dest->state = pesp_source->state; + if (pesp_source->type == ESPCONN_TCP){ + pesp_dest->proto.tcp->remote_port = pesp_source->proto.tcp->remote_port; + pesp_dest->proto.tcp->local_port = pesp_source->proto.tcp->local_port; + os_memcpy(pesp_dest->proto.tcp->remote_ip, pesp_source->proto.tcp->remote_ip, 4); + os_memcpy(pesp_dest->proto.tcp->local_ip, pesp_source->proto.tcp->local_ip, 4); + pesp_dest->proto.tcp->connect_callback = pesp_source->proto.tcp->connect_callback; + pesp_dest->proto.tcp->reconnect_callback = pesp_source->proto.tcp->reconnect_callback; + pesp_dest->proto.tcp->disconnect_callback = pesp_source->proto.tcp->disconnect_callback; + } else { + pesp_dest->proto.udp->remote_port = pesp_source->proto.udp->remote_port; + pesp_dest->proto.udp->local_port = pesp_source->proto.udp->local_port; + os_memcpy(pesp_dest->proto.udp->remote_ip, pesp_source->proto.udp->remote_ip, 4); + os_memcpy(pesp_dest->proto.udp->local_ip, pesp_source->proto.udp->local_ip, 4); + } + pesp_dest->recv_callback = pesp_source->recv_callback; + pesp_dest->sent_callback = pesp_source->sent_callback; + pesp_dest->link_cnt = pesp_source->link_cnt; + pesp_dest->reverse = pesp_source->reverse; +} + +/****************************************************************************** + * FunctionName : espconn_copy_partial + * Description : insert the node to the active connection list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_list_creat(espconn_msg **phead, espconn_msg* pinsert) +{ + espconn_msg *plist = NULL; +// espconn_msg *ptest = NULL; + if (*phead == NULL) + *phead = pinsert; + else { + plist = *phead; + while (plist->pnext != NULL) { + plist = plist->pnext; + } + plist->pnext = pinsert; + } + pinsert->pnext = NULL; + +/* ptest = *phead; + while(ptest != NULL){ + os_printf("espconn_list_creat %p\n", ptest); + ptest = ptest->pnext; + }*/ +} + +/****************************************************************************** + * FunctionName : espconn_list_delete + * Description : remove the node from the active connection list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_list_delete(espconn_msg **phead, espconn_msg* pdelete) +{ + espconn_msg *plist = NULL; +// espconn_msg *ptest = NULL; + plist = *phead; + if (plist == NULL){ + *phead = NULL; + } else { + if (plist == pdelete){ + *phead = plist->pnext; + } else { + while (plist != NULL) { + if (plist->pnext == pdelete){ + plist->pnext = pdelete->pnext; + } + plist = plist->pnext; + } + } + } +/* ptest = *phead; + while(ptest != NULL){ + os_printf("espconn_list_delete %p\n", ptest); + ptest = ptest->pnext; + }*/ +} + +/****************************************************************************** + * FunctionName : espconn_find_connection + * Description : Initialize the server: set up a listening PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build server + * Returns : none + *******************************************************************************/ +bool ICACHE_FLASH_ATTR espconn_find_connection(struct espconn *pespconn, espconn_msg **pnode) +{ + espconn_msg *plist = NULL; + struct ip_addr ip_remot; + struct ip_addr ip_list; + plist = plink_active; + + while(plist != NULL){ + if (pespconn == plist->pespconn){ + *pnode = plist; + return true; + } else { + IP4_ADDR(&ip_remot, pespconn->proto.tcp->remote_ip[0], pespconn->proto.tcp->remote_ip[1], + pespconn->proto.tcp->remote_ip[2], pespconn->proto.tcp->remote_ip[3]); + + IP4_ADDR(&ip_list, plist->pcommon.remote_ip[0], plist->pcommon.remote_ip[1], + plist->pcommon.remote_ip[2], plist->pcommon.remote_ip[3]); + if ((ip_list.addr == ip_remot.addr) && (pespconn->proto.tcp->remote_port == plist->pcommon.remote_port)){ + *pnode = plist; + return true; + } + } + plist = plist ->pnext; + } + return false; +} + +/****************************************************************************** + * FunctionName : espconn_connect + * Description : The function given as the connect + * Parameters : espconn -- the espconn used to listen the connection + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_connect(struct espconn *espconn) +{ + struct ip_addr ipaddr; + struct ip_info ipinfo; + uint8 connect_status = 0; + sint8 value = ESPCONN_OK; + espconn_msg *plist = NULL; + + if (espconn == NULL) { + return ESPCONN_ARG; + } else if (espconn ->type != ESPCONN_TCP) + return ESPCONN_ARG; + + if (wifi_get_opmode() == ESPCONN_STA){ + wifi_get_ip_info(STA_NETIF,&ipinfo); + if (ipinfo.ip.addr == 0){ + return ESPCONN_RTE; + } + } else if(wifi_get_opmode() == ESPCONN_AP){ + wifi_get_ip_info(AP_NETIF,&ipinfo); + if (ipinfo.ip.addr == 0){ + return ESPCONN_RTE; + } + } else if(wifi_get_opmode() == ESPCONN_AP_STA){ + IP4_ADDR(&ipaddr, espconn->proto.tcp->remote_ip[0], + espconn->proto.tcp->remote_ip[1], + espconn->proto.tcp->remote_ip[2], + espconn->proto.tcp->remote_ip[3]); + ipaddr.addr <<= 8; + wifi_get_ip_info(AP_NETIF,&ipinfo); + ipinfo.ip.addr <<= 8; + espconn_printf("softap_addr = %x, remote_addr = %x\n", ipinfo.ip.addr, ipaddr.addr); + + if (ipaddr.addr != ipinfo.ip.addr){ + connect_status = wifi_station_get_connect_status(); + if (connect_status == STATION_GOT_IP){ + wifi_get_ip_info(STA_NETIF,&ipinfo); + if (ipinfo.ip.addr == 0) + return ESPCONN_RTE; + } else { + return connect_status; + } + } + } + + for (plist = plink_active; plist != NULL; plist = plist->pnext){ + if (plist->pespconn->type == ESPCONN_TCP){ + if (espconn->proto.tcp->local_port == plist->pespconn->proto.tcp->local_port){ + return ESPCONN_ISCONN; + } + } + } + + value = espconn_tcp_client(espconn); + + return value; +} + +/****************************************************************************** + * FunctionName : espconn_create + * Description : sent data for client or server + * Parameters : espconn -- espconn to the data transmission + * Returns : result +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_create(struct espconn *espconn) +{ + sint8 value = ESPCONN_OK; + espconn_msg *plist = NULL; + + if (espconn == NULL) { + return ESPCONN_ARG; + } else if (espconn ->type != ESPCONN_UDP){ + return ESPCONN_ARG; + } + + for (plist = plink_active; plist != NULL; plist = plist->pnext){ + if (plist->pespconn->type == ESPCONN_UDP){ + if (espconn->proto.udp->local_port == plist->pespconn->proto.udp->local_port){ + return ESPCONN_ISCONN; + } + } + } + + value = espconn_udp_server(espconn); + + return value; +} + +/****************************************************************************** + * FunctionName : espconn_sent + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_sent(struct espconn *espconn, uint8 *psent, uint16 length) +{ + espconn_msg *pnode = NULL; + bool value = false; + if (espconn == NULL) { + return ESPCONN_ARG; + } + espconn ->state = ESPCONN_WRITE; + value = espconn_find_connection(espconn, &pnode); + switch (espconn ->type) { + case ESPCONN_TCP: + if (value && (pnode->pcommon.write_len == pnode->pcommon.write_total)){ + espconn_tcp_sent(pnode, psent, length); + }else + return ESPCONN_ARG; + break; + + case ESPCONN_UDP: { + if (value) + espconn_udp_sent(pnode, psent, length); + else + return ESPCONN_ARG; + break; + } + + default : + break; + } + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_con + * Description : get the number of simulatenously active TCP connections + * Parameters : espconn -- espconn to set the connect callback + * Returns : none +*******************************************************************************/ +uint8 ICACHE_FLASH_ATTR espconn_tcp_get_max_con(void) +{ + uint8 tcp_num = 0; + + tcp_num = MEMP_NUM_TCP_PCB; + + return tcp_num; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_con + * Description : set the number of simulatenously active TCP connections + * Parameters : espconn -- espconn to set the connect callback + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_tcp_set_max_con(uint8 num) +{ + if (num == 0) + return ESPCONN_ARG; + + MEMP_NUM_TCP_PCB = num; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_con_allow + * Description : get the count of simulatenously active connections on the server + * Parameters : espconn -- espconn to get the count + * Returns : result +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_tcp_get_max_con_allow(struct espconn *espconn) +{ + espconn_msg *pget_msg = NULL; + if ((espconn == NULL) || (espconn->type == ESPCONN_UDP)) + return ESPCONN_ARG; + + pget_msg = pserver_list; + while (pget_msg != NULL){ + if (pget_msg->pespconn == espconn){ + return pget_msg->count_opt; + } + pget_msg = pget_msg->pnext; + } + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_con_allow + * Description : set the count of simulatenously active connections on the server + * Parameters : espconn -- espconn to set the count + * Returns : result +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_tcp_set_max_con_allow(struct espconn *espconn, uint8 num) +{ + espconn_msg *pset_msg = NULL; + if ((espconn == NULL) || (num > MEMP_NUM_TCP_PCB) || (espconn->type == ESPCONN_UDP)) + return ESPCONN_ARG; + + pset_msg = pserver_list; + while (pset_msg != NULL){ + if (pset_msg->pespconn == espconn){ + pset_msg->count_opt = num; + return ESPCONN_OK; + } + pset_msg = pset_msg->pnext; + } + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_regist_sentcb + * Description : Used to specify the function that should be called when data + * has been successfully delivered to the remote host. + * Parameters : espconn -- espconn to set the sent callback + * sent_cb -- sent callback function to call for this espconn + * when data is successfully sent + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_regist_sentcb(struct espconn *espconn, espconn_sent_callback sent_cb) +{ + if (espconn == NULL) { + return ESPCONN_ARG; + } + + espconn ->sent_callback = sent_cb; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_regist_connectcb + * Description : used to specify the function that should be called when + * connects to host. + * Parameters : espconn -- espconn to set the connect callback + * connect_cb -- connected callback function to call when connected + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_regist_connectcb(struct espconn *espconn, espconn_connect_callback connect_cb) +{ + if (espconn == NULL) { + return ESPCONN_ARG; + } + + espconn->proto.tcp->connect_callback = connect_cb; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_regist_recvcb + * Description : used to specify the function that should be called when recv + * data from host. + * Parameters : espconn -- espconn to set the recv callback + * recv_cb -- recv callback function to call when recv data + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_regist_recvcb(struct espconn *espconn, espconn_recv_callback recv_cb) +{ + if (espconn == NULL) { + return ESPCONN_ARG; + } + + espconn ->recv_callback = recv_cb; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_regist_reconcb + * Description : used to specify the function that should be called when connection + * because of err disconnect. + * Parameters : espconn -- espconn to set the err callback + * recon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_regist_reconcb(struct espconn *espconn, espconn_reconnect_callback recon_cb) +{ + if (espconn == NULL) { + return ESPCONN_ARG; + } + + espconn ->proto.tcp->reconnect_callback = recon_cb; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_regist_disconcb + * Description : used to specify the function that should be called when disconnect + * Parameters : espconn -- espconn to set the err callback + * discon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_regist_disconcb(struct espconn *espconn, espconn_connect_callback discon_cb) +{ + if (espconn == NULL) { + return ESPCONN_ARG; + } + + espconn ->proto.tcp->disconnect_callback = discon_cb; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_get_connection_info + * Description : used to specify the function that should be called when disconnect + * Parameters : espconn -- espconn to set the err callback + * discon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_get_connection_info(struct espconn *pespconn, remot_info **pcon_info, uint8 typeflags) +{ + espconn_msg *plist = NULL; + + if (pespconn == NULL) + return ESPCONN_ARG; + + os_memset(premot, 0, sizeof(premot)); + pespconn->link_cnt = 0; + plist = plink_active; + switch (pespconn->type){ + case ESPCONN_TCP: + while(plist != NULL){ + if ((plist->pespconn->type == ESPCONN_TCP) && (plist->preverse == pespconn)){ + switch (typeflags){ + case ESPCONN_SSL: + if (plist->pssl != NULL){ + premot[pespconn->link_cnt].state = plist->pespconn->state; + premot[pespconn->link_cnt].remote_port = plist->pcommon.remote_port; + os_memcpy(premot[pespconn->link_cnt].remote_ip, plist->pcommon.remote_ip, 4); + pespconn->link_cnt ++; + } + break; + case ESPCONN_NORM: + if (plist->pssl == NULL){ + premot[pespconn->link_cnt].state = plist->pespconn->state; + premot[pespconn->link_cnt].remote_port = plist->pcommon.remote_port; + os_memcpy(premot[pespconn->link_cnt].remote_ip, plist->pcommon.remote_ip, 4); + pespconn->link_cnt ++; + } + break; + default: + break; + } + } + plist = plist->pnext; + } + + break; + case ESPCONN_UDP: + while(plist != NULL){ + if (plist->pespconn->type == ESPCONN_UDP){ + premot[pespconn->link_cnt].state = plist->pespconn->state; + premot[pespconn->link_cnt].remote_port = plist->pcommon.remote_port; + os_memcpy(premot[pespconn->link_cnt].remote_ip, plist->pcommon.remote_ip, 4); + pespconn->link_cnt ++; + } + plist = plist->pnext; + } + break; + default: + break; + } + *pcon_info = premot; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_accept + * Description : The function given as the listen + * Parameters : espconn -- the espconn used to listen the connection + * Returns : +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_accept(struct espconn *espconn) +{ + sint8 value = ESPCONN_OK; + espconn_msg *plist = NULL; + + if (espconn == NULL) { + return ESPCONN_ARG; + } else if (espconn ->type != ESPCONN_TCP) + return ESPCONN_ARG; + + for (plist = plink_active; plist != NULL; plist = plist->pnext){ + if (plist->pespconn->type == ESPCONN_TCP){ + if (espconn->proto.tcp->local_port == plist->pespconn->proto.tcp->local_port){ + return ESPCONN_ISCONN; + } + } + } + value = espconn_tcp_server(espconn); + + return value; +} + +/****************************************************************************** + * FunctionName : espconn_regist_time + * Description : used to specify the time that should be called when don't recv data + * Parameters : espconn -- the espconn used to the connection + * interval -- the timer when don't recv data + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_regist_time(struct espconn *espconn, uint32 interval, uint8 type_flag) +{ + espconn_msg *pnode = NULL; + bool value = false; + if ((espconn == NULL) || (type_flag > 0x01)) + return ESPCONN_ARG; + + if (type_flag == 0x01){ + value = espconn_find_connection(espconn, &pnode); + if (value){ + pnode->pcommon.timeout = interval; + return ESPCONN_OK; + } else + return ESPCONN_ARG; + } else { + link_timer = interval; + os_printf("espconn_regist_time %d\n", link_timer); + return ESPCONN_OK; + } +} + +/****************************************************************************** + * FunctionName : espconn_disconnect + * Description : disconnect with host + * Parameters : espconn -- the espconn used to disconnect the connection + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_disconnect(struct espconn *espconn) +{ + espconn_msg *pnode = NULL; + bool value = false; + + if (espconn == NULL) { + return ESPCONN_ARG;; + } else if (espconn ->type != ESPCONN_TCP) + return ESPCONN_ARG; + + value = espconn_find_connection(espconn, &pnode); + + if (value){ + espconn_tcp_disconnect(pnode); + return ESPCONN_OK; + } else + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_set_opt + * Description : access port value for client so that we don't end up bouncing + * all connections at the same time . + * Parameters : none + * Returns : access port value +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_set_opt(struct espconn *espconn, uint8 opt) +{ + espconn_msg *pnode = NULL; + bool value = false; + + if (espconn == NULL || opt > ESPCONN_END) { + return ESPCONN_ARG;; + } else if (espconn->type != ESPCONN_TCP) + return ESPCONN_ARG; + + value = espconn_find_connection(espconn, &pnode); + if (value) { + pnode->pcommon.espconn_opt = opt; + return ESPCONN_OK; + } else + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_delete + * Description : disconnect with host + * Parameters : espconn -- the espconn used to disconnect the connection + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_delete(struct espconn *espconn) +{ + espconn_msg *pnode = NULL; + bool value = false; + + if (espconn == NULL) { + return ESPCONN_ARG; + } else if (espconn ->type != ESPCONN_UDP) + return espconn_tcp_delete(espconn); + + value = espconn_find_connection(espconn, &pnode); + + if (value){ + espconn_udp_disconnect(pnode); + return ESPCONN_OK; + } else + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_port + * Description : access port value for client so that we don't end up bouncing + * all connections at the same time . + * Parameters : none + * Returns : access port value +*******************************************************************************/ +uint32 espconn_port(void) +{ + uint32 port = 0; + static uint32 randnum = 0; + + do { + port = system_get_time(); + + if (port < 0) { + port = os_random() - port; + } + + port %= 0xc350; + + if (port < 0x400) { + port += 0x400; + } + + } while (port == randnum); + + randnum = port; + + return port; +} + +/****************************************************************************** + * FunctionName : espconn_gethostbyname + * Description : Resolve a hostname (string) into an IP address. + * Parameters : pespconn -- espconn to resolve a hostname + * hostname -- the hostname that is to be queried + * addr -- pointer to a ip_addr_t where to store the address if + * it is already cached in the dns_table (only valid if + * ESPCONN_OK is returned!) + * found -- a callback function to be called on success, failure + * or timeout (only if ERR_INPROGRESS is returned!) + * Returns : err_t return code + * - ESPCONN_OK if hostname is a valid IP address string or the host + * name is already in the local names table. + * - ESPCONN_INPROGRESS enqueue a request to be sent to the DNS server + * for resolution if no errors are present. + * - ESPCONN_ARG: dns client not initialized or invalid hostname +*******************************************************************************/ +err_t ICACHE_FLASH_ATTR +espconn_gethostbyname(struct espconn *pespconn, const char *hostname, ip_addr_t *addr, dns_found_callback found) +{ + return dns_gethostbyname(hostname, addr, found, pespconn); +} + diff --git a/app/lwip/app/espconn_tcp.c b/app/lwip/app/espconn_tcp.c new file mode 100644 index 00000000..df92929a --- /dev/null +++ b/app/lwip/app/espconn_tcp.c @@ -0,0 +1,954 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: espconn_tcp.c + * + * Description: tcp proto interface + * + * Modification history: + * 2014/3/31, v1.0 create this file. +*******************************************************************************/ + +#include "lwip/netif.h" +#include "lwip/inet.h" +#include "netif/etharp.h" +#include "lwip/tcp.h" +#include "lwip/ip.h" +#include "lwip/init.h" +#include "lwip/tcp_impl.h" +#include "lwip/memp.h" + +#include "ets_sys.h" +#include "os_type.h" +//#include "os.h" +#include "lwip/mem.h" +#include "lwip/app/espconn_tcp.h" + +extern espconn_msg *plink_active; +extern uint32 link_timer; +extern espconn_msg *pserver_list; + +static err_t +espconn_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); +static void +espconn_client_close(void *arg, struct tcp_pcb *pcb); + +static err_t +espconn_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); +static void +espconn_server_close(void *arg, struct tcp_pcb *pcb); + +///////////////////////////////common function///////////////////////////////// + +/****************************************************************************** + * FunctionName : espconn_tcp_reconnect + * Description : reconnect with host + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_tcp_reconnect(void *arg) +{ + espconn_msg *precon_cb = arg; + sint8 re_err = 0; + if (precon_cb != NULL) { + struct espconn *espconn = precon_cb->preverse; + re_err = precon_cb->pcommon.err; + if (precon_cb->pespconn != NULL){ + if (espconn != NULL){ + if (precon_cb->pespconn->proto.tcp != NULL){ + espconn_copy_partial(espconn, precon_cb->pespconn); + espconn_printf("server: %d.%d.%d.%d : %d reconnection\n", espconn->proto.tcp->remote_ip[0], + espconn->proto.tcp->remote_ip[1],espconn->proto.tcp->remote_ip[2], + espconn->proto.tcp->remote_ip[3],espconn->proto.tcp->remote_port); + os_free(precon_cb->pespconn->proto.tcp); + precon_cb->pespconn->proto.tcp = NULL; + } + os_free(precon_cb->pespconn); + precon_cb->pespconn = NULL; + } else { + espconn = precon_cb->pespconn; + espconn_printf("client: %d.%d.%d.%d : %d reconnection\n", espconn->proto.tcp->local_ip[0], + espconn->proto.tcp->local_ip[1],espconn->proto.tcp->local_ip[2], + espconn->proto.tcp->local_ip[3],espconn->proto.tcp->local_port); + } + } + os_free(precon_cb); + precon_cb = NULL; + + if (espconn->proto.tcp->reconnect_callback != NULL) { + espconn->proto.tcp->reconnect_callback(espconn, re_err); + } + } else { + espconn_printf("espconn_tcp_reconnect err\n"); + } +} + +/****************************************************************************** + * FunctionName : espconn_tcp_disconnect + * Description : disconnect with host + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_tcp_disconnect_successful(void *arg) +{ + espconn_msg *pdiscon_cb = arg; + sint8 dis_err = 0; + if (pdiscon_cb != NULL) { + struct espconn *espconn = pdiscon_cb->preverse; + + dis_err = pdiscon_cb->pcommon.err; + if (pdiscon_cb->pespconn != NULL){ + struct tcp_pcb *pcb = NULL; + if (espconn != NULL){ + if (pdiscon_cb->pespconn->proto.tcp != NULL){ + espconn_copy_partial(espconn, pdiscon_cb->pespconn); + espconn_printf("server: %d.%d.%d.%d : %d disconnect\n", espconn->proto.tcp->remote_ip[0], + espconn->proto.tcp->remote_ip[1],espconn->proto.tcp->remote_ip[2], + espconn->proto.tcp->remote_ip[3],espconn->proto.tcp->remote_port); + os_free(pdiscon_cb->pespconn->proto.tcp); + pdiscon_cb->pespconn->proto.tcp = NULL; + } + os_free(pdiscon_cb->pespconn); + pdiscon_cb->pespconn = NULL; + } else { + espconn = pdiscon_cb->pespconn; + espconn_printf("client: %d.%d.%d.%d : %d disconnect\n", espconn->proto.tcp->local_ip[0], + espconn->proto.tcp->local_ip[1],espconn->proto.tcp->local_ip[2], + espconn->proto.tcp->local_ip[3],espconn->proto.tcp->local_port); + } + pcb = pdiscon_cb->pcommon.pcb; + tcp_arg(pcb, NULL); + tcp_err(pcb, NULL); + /*delete TIME_WAIT State pcb after 2MSL time,for not all data received by application.*/ + if (pdiscon_cb->pcommon.espconn_opt == ESPCONN_REUSEADDR){ + if (pcb->state == TIME_WAIT){ + tcp_pcb_remove(&tcp_tw_pcbs,pcb); + memp_free(MEMP_TCP_PCB,pcb); + } + } + } + os_free(pdiscon_cb); + pdiscon_cb = NULL; + + if (espconn->proto.tcp->disconnect_callback != NULL) { + espconn->proto.tcp->disconnect_callback(espconn); + } + } else { + espconn_printf("espconn_tcp_disconnect err\n"); + } +} + +/****************************************************************************** + * FunctionName : espconn_tcp_sent + * Description : sent data for client or server + * Parameters : void *arg -- client or server to send + * uint8* psent -- Data to send + * uint16 length -- Length of data to send + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_tcp_sent(void *arg, uint8 *psent, uint16 length) +{ + espconn_msg *ptcp_sent = arg; + struct tcp_pcb *pcb = NULL; + err_t err = 0; + u16_t len = 0; + u8_t data_to_send = false; + + espconn_printf("espconn_tcp_sent ptcp_sent %p psent %p length %d\n", ptcp_sent, psent, length); + + if (ptcp_sent == NULL || psent == NULL || length == 0) { + return; + } + + pcb = ptcp_sent->pcommon.pcb; + if (tcp_sndbuf(pcb) < length) { + len = tcp_sndbuf(pcb); + } else { + len = length; + LWIP_ASSERT("length did not fit into uint16!", (len == length)); + } + + if (len > (2*pcb->mss)) { + len = 2*pcb->mss; + } + + do { + espconn_printf("espconn_tcp_sent writing %d bytes %p\n", len, pcb); + err = tcp_write(pcb, psent, len, 0); + + if (err == ERR_MEM) { + len /= 2; + } + } while (err == ERR_MEM && len > 1); + + if (err == ERR_OK) { + data_to_send = true; + ptcp_sent->pcommon.ptrbuf = psent + len; + ptcp_sent->pcommon.cntr = length - len; + ptcp_sent->pcommon.write_len = len; + espconn_printf("espconn_tcp_sent sending %d bytes, remain %d\n", len, ptcp_sent->pcommon.cntr); + } + + if (data_to_send == true) { + err = tcp_output(pcb); + } else { + ptcp_sent->pespconn ->state = ESPCONN_CLOSE; + } +} + +/****************************************************************************** + * FunctionName : espconn_close + * Description : The connection has been successfully closed. + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_tcp_disconnect(espconn_msg *pdiscon) +{ + if (pdiscon != NULL){ + if (pdiscon->preverse != NULL) + espconn_server_close(pdiscon, pdiscon->pcommon.pcb); + else + espconn_client_close(pdiscon, pdiscon->pcommon.pcb); + } else{ + espconn_printf("espconn_server_disconnect err.\n"); + } +} + +///////////////////////////////client function///////////////////////////////// +/****************************************************************************** + * FunctionName : espconn_close + * Description : The connection has been successfully closed. + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_cclose_cb(void *arg) +{ + espconn_msg *pclose_cb = arg; + + if (pclose_cb == NULL) { + return; + } + + struct tcp_pcb *pcb = pclose_cb->pcommon.pcb; + + espconn_printf("espconn_close %d %d\n", pcb->state, pcb->nrtx); + + if (pcb->state == TIME_WAIT || pcb->state == CLOSED) { + pclose_cb ->pespconn ->state = ESPCONN_CLOSE; + /*remove the node from the client's active connection list*/ + espconn_list_delete(&plink_active, pclose_cb); + espconn_tcp_disconnect_successful((void*)pclose_cb); + } else { + os_timer_arm(&pclose_cb->pcommon.ptimer, TCP_FAST_INTERVAL, 0); + } +} + +/****************************************************************************** + * FunctionName : espconn_client_close + * Description : The connection shall be actively closed. + * Parameters : pcb -- Additional argument to pass to the callback function + * pcb -- the pcb to close + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_client_close(void *arg, struct tcp_pcb *pcb) +{ + err_t err; + espconn_msg *pclose = arg; + + os_timer_disarm(&pclose->pcommon.ptimer); + + tcp_recv(pcb, NULL); + err = tcp_close(pcb); + os_timer_setfn(&pclose->pcommon.ptimer, espconn_cclose_cb, pclose); + os_timer_arm(&pclose->pcommon.ptimer, TCP_FAST_INTERVAL, 0); + + if (err != ERR_OK) { + /* closing failed, try again later */ + tcp_recv(pcb, espconn_client_recv); + } else { + /* closing succeeded */ + tcp_sent(pcb, NULL); + } +} + +/****************************************************************************** + * FunctionName : espconn_client_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 ICACHE_FLASH_ATTR +espconn_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + espconn_msg *precv_cb = arg; + + tcp_arg(pcb, arg); + + if (p != NULL) { + tcp_recved(pcb, p ->tot_len); + } + + if (err == ERR_OK && p != NULL) { + char *pdata = NULL; + u16_t length = 0; + pdata = (char *)os_zalloc(p ->tot_len + 1); + length = pbuf_copy_partial(p, pdata, p ->tot_len, 0); + pbuf_free(p); + + if (length != 0) { + precv_cb->pespconn ->state = ESPCONN_READ; + precv_cb->pcommon.pcb = pcb; + if (precv_cb->pespconn->recv_callback != NULL) { + precv_cb->pespconn->recv_callback(precv_cb->pespconn, pdata, length); + } + precv_cb->pespconn ->state = ESPCONN_CONNECT; + } + + os_free(pdata); + pdata = NULL; + } + + if (err == ERR_OK && p == NULL) { + espconn_client_close(precv_cb, pcb); + } + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_client_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 ICACHE_FLASH_ATTR +espconn_client_sent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + espconn_msg *psent_cb = arg; + + psent_cb->pcommon.pcb = pcb; + psent_cb->pcommon.write_total += len; + espconn_printf("espconn_client_sent sent %d %d\n", len, psent_cb->pcommon.write_total); + if (psent_cb->pcommon.write_total == psent_cb->pcommon.write_len){ + psent_cb->pcommon.write_total = 0; + psent_cb->pcommon.write_len = 0; + if (psent_cb->pcommon.cntr == 0) { + psent_cb->pespconn->state = ESPCONN_CONNECT; + + if (psent_cb->pespconn->sent_callback != NULL) { + psent_cb->pespconn->sent_callback(psent_cb->pespconn); + } + } else + espconn_tcp_sent(psent_cb, psent_cb->pcommon.ptrbuf, psent_cb->pcommon.cntr); + } else { + + } + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_client_poll + * Description : The poll function is called every 2nd second. + * If there has been no data sent (which resets the retries) in 8 seconds, close. + * If the last portion of a file has not been sent in 2 seconds, close. + * + * 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 ICACHE_FLASH_ATTR +espconn_client_poll(void *arg, struct tcp_pcb *pcb) +{ + espconn_msg *ppoll_cb = arg; + espconn_printf("espconn_client_poll pcb %p %p %d\n", pcb, arg, pcb->state); + if (arg == NULL) { + tcp_abandon(pcb, 0); + tcp_poll(pcb, NULL, 0); + return ERR_ABRT; + } + ppoll_cb->pcommon.pcb = pcb; + if (pcb->state == ESTABLISHED) { + } else { + tcp_poll(pcb, espconn_client_poll, 0); + espconn_client_close(ppoll_cb->pespconn, pcb); + } + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_client_err + * 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 ICACHE_FLASH_ATTR +espconn_client_err(void *arg, err_t err) +{ + espconn_msg *perr_cb = arg; + struct tcp_pcb *pcb = NULL; + LWIP_UNUSED_ARG(err); + + if (perr_cb != NULL) { + os_timer_disarm(&perr_cb->pcommon.ptimer); + pcb = perr_cb->pcommon.pcb; + perr_cb->pespconn->state = ESPCONN_CLOSE; + espconn_printf("espconn_client_err %d %d %d\n", pcb->state, pcb->nrtx, err); + + /*remove the node from the client's active connection list*/ + espconn_list_delete(&plink_active, perr_cb); + + if (err == ERR_ABRT) { + switch (pcb->state) { + case SYN_SENT: + if (pcb->nrtx == TCP_SYNMAXRTX) { + perr_cb->pcommon.err = ESPCONN_CONN; + } else { + perr_cb->pcommon.err = err; + } + + break; + + case ESTABLISHED: + if (pcb->nrtx == TCP_MAXRTX) { + perr_cb->pcommon.err = ESPCONN_TIMEOUT; + } else { + perr_cb->pcommon.err = err; + } + break; + + case FIN_WAIT_1: + if (pcb->nrtx == TCP_MAXRTX) { + perr_cb->pcommon.err = ESPCONN_CLSD; + } else { + perr_cb->pcommon.err = err; + } + break; + case FIN_WAIT_2: + perr_cb->pcommon.err = ESPCONN_CLSD; + break; + case CLOSED: + perr_cb->pcommon.err = ESPCONN_CONN; + break; + } + } else { + perr_cb->pcommon.err = err; + } + os_timer_setfn(&perr_cb->pcommon.ptimer, + (os_timer_func_t *) espconn_tcp_reconnect, perr_cb); + os_timer_arm(&perr_cb->pcommon.ptimer, 10, 0); + } +} + +/****************************************************************************** + * FunctionName : espconn_client_connect + * Description : A new incoming connection has been connected. + * Parameters : arg -- Additional argument to pass to the callback function + * tpcb -- The connection pcb which is connected + * err -- An unused error code, always ERR_OK currently + * Returns : connection result +*******************************************************************************/ +static err_t ICACHE_FLASH_ATTR +espconn_client_connect(void *arg, struct tcp_pcb *tpcb, err_t err) +{ + espconn_msg *pcon = arg; + espconn_printf("espconn_client_connect pcon %p tpcb %p\n", pcon, tpcb); + if (err == ERR_OK){ + pcon->pespconn->state = ESPCONN_CONNECT; + pcon->pcommon.err = err; + pcon->pcommon.pcb = tpcb; + tcp_arg(tpcb, (void *)pcon); + tcp_sent(tpcb, espconn_client_sent); + tcp_recv(tpcb, espconn_client_recv); + + //tcp_poll(tpcb, espconn_client_poll, 1); + if (pcon->pespconn->proto.tcp->connect_callback != NULL) { + pcon->pespconn->proto.tcp->connect_callback(pcon->pespconn); + } + } else{ + os_printf("err in host connected (%s)\n",lwip_strerr(err)); + } + return err; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_client + * Description : Initialize the client: set up a connect PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build client + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_tcp_client(struct espconn *espconn) +{ + struct tcp_pcb *pcb = NULL; + struct ip_addr ipaddr; + espconn_msg *pclient = NULL; + + pclient = (espconn_msg *)os_zalloc(sizeof(espconn_msg)); + if (pclient == NULL){ + return ESPCONN_MEM; + } + + IP4_ADDR(&ipaddr, espconn->proto.tcp->remote_ip[0], + espconn->proto.tcp->remote_ip[1], + espconn->proto.tcp->remote_ip[2], + espconn->proto.tcp->remote_ip[3]); + + pcb = tcp_new(); + + if (pcb == NULL) { +// pclient ->pespconn ->state = ESPCONN_NONE; + os_free(pclient); + pclient = NULL; + return ESPCONN_MEM; + } else { + /*insert the node to the active connection list*/ + espconn_list_creat(&plink_active, pclient); + tcp_arg(pcb, (void *)pclient); + tcp_err(pcb, espconn_client_err); + pclient->preverse = NULL; + pclient->pespconn = espconn; + pclient->pespconn->state = ESPCONN_WAIT; + pclient->pcommon.pcb = pcb; + tcp_bind(pcb, IP_ADDR_ANY, pclient->pespconn->proto.tcp->local_port); + pclient->pcommon.err = tcp_connect(pcb, &ipaddr, + pclient->pespconn->proto.tcp->remote_port, espconn_client_connect); + return pclient->pcommon.err; + } +} + +///////////////////////////////server function///////////////////////////////// +/****************************************************************************** + * FunctionName : espconn_closed + * Description : The connection has been successfully closed. + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_sclose_cb(void *arg) +{ + espconn_msg *psclose_cb = arg; + if (psclose_cb == NULL) { + return; + } + + struct tcp_pcb *pcb = psclose_cb ->pcommon.pcb; + espconn_printf("espconn_sclose_cb %d %d\n", pcb->state, pcb->nrtx); + if (pcb->state == CLOSED || pcb->state == TIME_WAIT) { + psclose_cb ->pespconn ->state = ESPCONN_CLOSE; + /*remove the node from the server's active connection list*/ + espconn_list_delete(&plink_active, psclose_cb); + espconn_tcp_disconnect_successful(psclose_cb); + } else { + os_timer_arm(&psclose_cb->pcommon.ptimer, TCP_FAST_INTERVAL, 0); + } +} + +/****************************************************************************** + * FunctionName : espconn_server_close + * Description : The connection shall be actively closed. + * Parameters : arg -- Additional argument to pass to the callback function + * pcb -- the pcb to close + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_server_close(void *arg, struct tcp_pcb *pcb) +{ + err_t err; + espconn_msg *psclose = arg; + + os_timer_disarm(&psclose->pcommon.ptimer); + + tcp_recv(pcb, NULL); + err = tcp_close(pcb); + os_timer_setfn(&psclose->pcommon.ptimer, espconn_sclose_cb, psclose); + os_timer_arm(&psclose->pcommon.ptimer, TCP_FAST_INTERVAL, 0); + + if (err != ERR_OK) { + /* closing failed, try again later */ + tcp_recv(pcb, espconn_server_recv); + } else { + /* closing succeeded */ + tcp_poll(pcb, NULL, 0); + tcp_sent(pcb, NULL); + } +} + +/****************************************************************************** + * FunctionName : espconn_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 ICACHE_FLASH_ATTR +espconn_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + espconn_msg *precv_cb = arg; + + tcp_arg(pcb, arg); + espconn_printf("server has application data received: %d\n", system_get_free_heap_size()); + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + } + + if (err == ERR_OK && p != NULL) { + u8_t *data_ptr = NULL; + u32_t data_cntr = 0; + precv_cb->pcommon.recv_check = 0; + data_ptr = (u8_t *)os_zalloc(p ->tot_len + 1); + data_cntr = pbuf_copy_partial(p, data_ptr, p ->tot_len, 0); + pbuf_free(p); + + if (data_cntr != 0) { + precv_cb->pespconn ->state = ESPCONN_READ; + precv_cb->pcommon.pcb = pcb; + if (precv_cb->pespconn->recv_callback != NULL) { + precv_cb->pespconn->recv_callback(precv_cb->pespconn, data_ptr, data_cntr); + } + precv_cb->pespconn ->state = ESPCONN_CONNECT; + } + + os_free(data_ptr); + data_ptr = NULL; + espconn_printf("server's application data has been processed: %d\n", system_get_free_heap_size()); + } else { + if (p != NULL) { + pbuf_free(p); + } + + espconn_server_close(precv_cb, pcb); + } + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_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 ICACHE_FLASH_ATTR +espconn_server_sent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + espconn_msg *psent_cb = arg; + + psent_cb->pcommon.pcb = pcb; + psent_cb->pcommon.recv_check = 0; + psent_cb->pcommon.write_total += len; + espconn_printf("espconn_server_sent sent %d %d\n", len, psent_cb->pcommon.write_total); + if (psent_cb->pcommon.write_total == psent_cb->pcommon.write_len){ + psent_cb->pcommon.write_total = 0; + psent_cb->pcommon.write_len = 0; + if (psent_cb ->pcommon.cntr == 0) { + psent_cb ->pespconn ->state = ESPCONN_CONNECT; + + if (psent_cb ->pespconn ->sent_callback != NULL) { + psent_cb ->pespconn ->sent_callback(psent_cb ->pespconn); + } + } else + espconn_tcp_sent(psent_cb, psent_cb ->pcommon.ptrbuf, psent_cb ->pcommon.cntr); + } else { + + } + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_server_poll + * Description : The poll function is called every 3nd second. + * If there has been no data sent (which resets the retries) in 3 seconds, close. + * If the last portion of a file has not been sent in 3 seconds, close. + * + * 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 ICACHE_FLASH_ATTR +espconn_server_poll(void *arg, struct tcp_pcb *pcb) +{ + espconn_msg *pspoll_cb = arg; + + if (arg == NULL) { + tcp_abandon(pcb, 0); + tcp_poll(pcb, NULL, 0); + return ERR_ABRT; + } + + espconn_printf("espconn_server_poll %d %d\n", pspoll_cb->pcommon.recv_check, pcb->state); + pspoll_cb->pcommon.pcb = pcb; + if (pcb->state == ESTABLISHED) { + pspoll_cb->pcommon.recv_check++; + if (pspoll_cb->pcommon.timeout != 0){ + if (pspoll_cb->pcommon.recv_check == pspoll_cb->pcommon.timeout) { + pspoll_cb->pcommon.recv_check = 0; + espconn_server_close(pspoll_cb, pcb); + } + } else if (link_timer != 0){ + if (pspoll_cb->pcommon.recv_check == link_timer) { + pspoll_cb->pcommon.recv_check = 0; + espconn_server_close(pspoll_cb, pcb); + } + } else { + if (pspoll_cb->pcommon.recv_check == 0x0a) { + pspoll_cb->pcommon.recv_check = 0; + espconn_server_close(pspoll_cb, pcb); + } + } + } else { + espconn_server_close(pspoll_cb, pcb); + } + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : esponn_server_err + * 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 ICACHE_FLASH_ATTR +esponn_server_err(void *arg, err_t err) +{ + espconn_msg *pserr_cb = arg; + struct tcp_pcb *pcb = NULL; + if (pserr_cb != NULL) { + os_timer_disarm(&pserr_cb->pcommon.ptimer); + pcb = pserr_cb->pcommon.pcb; + pserr_cb->pespconn->state = ESPCONN_CLOSE; + + /*remove the node from the server's active connection list*/ + espconn_list_delete(&plink_active, pserr_cb); + + if (err == ERR_ABRT) { + switch (pcb->state) { + case SYN_RCVD: + if (pcb->nrtx == TCP_SYNMAXRTX) { + pserr_cb->pcommon.err = ESPCONN_CONN; + } else { + pserr_cb->pcommon.err = err; + } + + break; + + case ESTABLISHED: + if (pcb->nrtx == TCP_MAXRTX) { + pserr_cb->pcommon.err = ESPCONN_TIMEOUT; + } else { + pserr_cb->pcommon.err = err; + } + + break; + + case CLOSE_WAIT: + if (pcb->nrtx == TCP_MAXRTX) { + pserr_cb->pcommon.err = ESPCONN_CLSD; + } else { + pserr_cb->pcommon.err = err; + } + break; + case LAST_ACK: + pserr_cb->pcommon.err = ESPCONN_CLSD; + break; + + case CLOSED: + pserr_cb->pcommon.err = ESPCONN_CONN; + break; + default : + break; + } + } else { + pserr_cb->pcommon.err = err; + } + + os_timer_setfn(&pserr_cb->pcommon.ptimer, + (os_timer_func_t *) espconn_tcp_reconnect, pserr_cb); + os_timer_arm(&pserr_cb->pcommon.ptimer, 10, 0); + } +} + +/****************************************************************************** + * FunctionName : espconn_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 ICACHE_FLASH_ATTR +espconn_tcp_accept(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct espconn *espconn = arg; + espconn_msg *paccept = NULL; + remot_info *pinfo = NULL; + LWIP_UNUSED_ARG(err); + + if(4096>system_get_free_heap_size()){ + return ERR_MEM; + } + + paccept = (espconn_msg *)os_zalloc(sizeof(espconn_msg)); + tcp_arg(pcb, paccept); + tcp_err(pcb, esponn_server_err); + if (paccept == NULL) + return ERR_MEM; + /*insert the node to the active connection list*/ + espconn_list_creat(&plink_active, paccept); + + paccept->preverse = espconn; + paccept->pespconn = (struct espconn *)os_zalloc(sizeof(struct espconn)); + if (paccept->pespconn == NULL) + return ERR_MEM; + paccept->pespconn->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); + if (paccept->pespconn->proto.tcp == NULL) + return ERR_MEM; + + //paccept->pcommon.timeout = 0x0a; + //link_timer = 0x0a; + + paccept->pcommon.pcb = pcb; + + paccept->pcommon.remote_port = pcb->remote_port; + paccept->pcommon.remote_ip[0] = ip4_addr1_16(&pcb->remote_ip); + paccept->pcommon.remote_ip[1] = ip4_addr2_16(&pcb->remote_ip); + paccept->pcommon.remote_ip[2] = ip4_addr3_16(&pcb->remote_ip); + paccept->pcommon.remote_ip[3] = ip4_addr4_16(&pcb->remote_ip); + + os_memcpy(espconn->proto.tcp->remote_ip, paccept->pcommon.remote_ip, 4); + espconn->proto.tcp->remote_port = pcb->remote_port; + espconn->state = ESPCONN_CONNECT; + espconn_copy_partial(paccept->pespconn, espconn); + espconn_get_connection_info(espconn, &pinfo , 0); + espconn_printf("espconn_tcp_accept link_cnt: %d\n", espconn->link_cnt); + if (espconn->link_cnt == espconn_tcp_get_max_con_allow(espconn) + 1) + return ERR_ISCONN; + + tcp_sent(pcb, espconn_server_sent); + tcp_recv(pcb, espconn_server_recv); + tcp_poll(pcb, espconn_server_poll, 8); /* every 1 seconds */ + + if (paccept->pespconn->proto.tcp->connect_callback != NULL) { + paccept->pespconn->proto.tcp->connect_callback(paccept->pespconn); + } + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_server + * Description : Initialize the server: set up a listening PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build server + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_tcp_server(struct espconn *espconn) +{ + struct tcp_pcb *pcb = NULL; + espconn_msg *pserver = NULL; + + pserver = (espconn_msg *)os_zalloc(sizeof(espconn_msg)); + if (pserver == NULL){ + return ESPCONN_MEM; + } + + pcb = tcp_new(); + if (pcb == NULL) { +// espconn ->state = ESPCONN_NONE; + os_free(pserver); + pserver = NULL; + return ESPCONN_MEM; + } else { + tcp_bind(pcb, IP_ADDR_ANY, espconn->proto.tcp->local_port); + pcb = tcp_listen(pcb); + if (pcb != NULL) { + /*insert the node to the active connection list*/ + espconn_list_creat(&pserver_list, pserver); + pserver->preverse = pcb; + pserver->pespconn = espconn; + pserver->count_opt = MEMP_NUM_TCP_PCB; + + espconn ->state = ESPCONN_LISTEN; + tcp_arg(pcb, (void *)espconn); +// tcp_err(pcb, esponn_server_err); + tcp_accept(pcb, espconn_tcp_accept); + return ESPCONN_OK; + } else { +// espconn ->state = ESPCONN_NONE; + os_free(pserver); + pserver = NULL; + return ESPCONN_MEM; + } + } +} + +/****************************************************************************** + * FunctionName : espconn_tcp_delete + * Description : delete the server: delete a listening PCB and free it + * Parameters : pdeletecon -- the espconn used to delete a server + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_tcp_delete(struct espconn *pdeletecon) +{ + err_t err; + remot_info *pinfo = NULL; + espconn_msg *pdelete_msg = NULL; + struct tcp_pcb *pcb = NULL; + + if (pdeletecon == NULL) + return ESPCONN_ARG; + + espconn_get_connection_info(pdeletecon, &pinfo , 0); + if (pdeletecon->link_cnt != 0) + return ESPCONN_INPROGRESS; + else { + os_printf("espconn_tcp_delete %p\n",pdeletecon); + pdelete_msg = pserver_list; + while (pdelete_msg != NULL){ + if (pdelete_msg->pespconn == pdeletecon){ + /*remove the node from the client's active connection list*/ + espconn_list_delete(&pserver_list, pdelete_msg); + pcb = pdelete_msg->preverse; + os_printf("espconn_tcp_delete %d\n",pcb->state); + err = tcp_close(pcb); + os_free(pdelete_msg); + pdelete_msg = NULL; + break; + } + pdelete_msg = pdelete_msg->pnext; + } + if (err == ERR_OK) + return err; + else + return ESPCONN_ARG; + } +} diff --git a/app/lwip/app/espconn_udp.c b/app/lwip/app/espconn_udp.c new file mode 100644 index 00000000..a9dd7f0c --- /dev/null +++ b/app/lwip/app/espconn_udp.c @@ -0,0 +1,298 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: espconn_udp.c + * + * Description: udp proto interface + * + * Modification history: + * 2014/3/31, v1.0 create this file. +*******************************************************************************/ + +#include "ets_sys.h" +#include "os_type.h" +//#include "os.h" + +#include "lwip/inet.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/mem.h" +#include "lwip/tcp_impl.h" +#include "lwip/udp.h" + +#include "lwip/app/espconn_udp.h" + +extern espconn_msg *plink_active; + +static void ICACHE_FLASH_ATTR espconn_data_sentcb(struct espconn *pespconn) +{ + if (pespconn == NULL) { + return; + } + + if (pespconn->sent_callback != NULL) { + pespconn->sent_callback(pespconn); + } +} + +static void ICACHE_FLASH_ATTR espconn_data_sent(void *arg) +{ + espconn_msg *psent = arg; + + if (psent == NULL) { + return; + } + + if (psent->pcommon.cntr == 0) { + psent->pespconn->state = ESPCONN_CONNECT; + sys_timeout(TCP_FAST_INTERVAL, espconn_data_sentcb, psent->pespconn); + } else { + espconn_udp_sent(arg, psent->pcommon.ptrbuf, psent->pcommon.cntr); + } +} + +/****************************************************************************** + * FunctionName : espconn_udp_sent + * Description : sent data for client or server + * Parameters : void *arg -- client or server to send + * uint8* psent -- Data to send + * uint16 length -- Length of data to send + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_udp_sent(void *arg, uint8 *psent, uint16 length) +{ + espconn_msg *pudp_sent = arg; + struct udp_pcb *upcb = pudp_sent->pcommon.pcb; + struct pbuf *p, *q; + u8_t *data = NULL; + u16_t cnt = 0; + u16_t datalen = 0; + u16_t i = 0; + err_t err; + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d %p\n", __LINE__, length, upcb)); + + if (pudp_sent == NULL || upcb == NULL || psent == NULL || length == 0) { + return; + } + + if (1024 < length) { + datalen = 1024; + } else { + datalen = length; + } + + p = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM); + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p)); + + if (p != NULL) { + q = p; + + while (q != NULL) { + data = (u8_t *)q->payload; + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, data)); + + for (i = 0; i < q->len; i++) { + data[i] = ((u8_t *) psent)[cnt++]; + } + + q = q->next; + } + } else { + return; + } + + upcb->remote_port = pudp_sent->pespconn->proto.udp->remote_port; + IP4_ADDR(&upcb->remote_ip, pudp_sent->pespconn->proto.udp->remote_ip[0], + pudp_sent->pespconn->proto.udp->remote_ip[1], + pudp_sent->pespconn->proto.udp->remote_ip[2], + pudp_sent->pespconn->proto.udp->remote_ip[3]); + + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %x %d\n", __LINE__, upcb->remote_ip, upcb->remote_port)); + err = udp_send(upcb, p); + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d\n", __LINE__, err)); + + if (p->ref != 0) { + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p)); + pbuf_free(p); + pudp_sent->pcommon.ptrbuf = psent + datalen; + pudp_sent->pcommon.cntr = length - datalen; + espconn_data_sent(pudp_sent); + } +} + +/****************************************************************************** + * FunctionName : espconn_udp_server_recv + * Description : This callback will be called when receiving a datagram. + * Parameters : arg -- user supplied argument + * upcb -- the udp_pcb which received data + * p -- the packet buffer that was received + * addr -- the remote IP address from which the packet was received + * port -- the remote port from which the packet was received + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, + struct ip_addr *addr, u16_t port) +{ + espconn_msg *precv = arg; + struct pbuf *q = NULL; + u8_t *pdata = NULL; + u16_t length = 0; + struct ip_info ipconfig; + + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_server_recv %d %p\n", __LINE__, upcb)); + + upcb->remote_port = port; + upcb->remote_ip = *addr; + + precv->pcommon.remote_ip[0] = ip4_addr1_16(&upcb->remote_ip); + precv->pcommon.remote_ip[1] = ip4_addr2_16(&upcb->remote_ip); + precv->pcommon.remote_ip[2] = ip4_addr3_16(&upcb->remote_ip); + precv->pcommon.remote_ip[3] = ip4_addr4_16(&upcb->remote_ip); + os_memcpy(precv->pespconn->proto.udp->remote_ip, precv->pcommon.remote_ip, 4); + precv->pespconn->proto.udp->remote_port = port; + precv->pcommon.remote_port = port; + precv->pcommon.pcb = upcb; + + if (wifi_get_opmode() != 1) { + wifi_get_ip_info(1, &ipconfig); + + if (!ip_addr_netcmp((struct ip_addr *)precv->pespconn->proto.udp->remote_ip, &ipconfig.ip, &ipconfig.netmask)) { + wifi_get_ip_info(0, &ipconfig); + } + } else { + wifi_get_ip_info(0, &ipconfig); + } + upcb->local_ip = ipconfig.ip; + precv->pespconn->proto.udp->local_ip[0] = ip4_addr1_16(&upcb->local_ip); + precv->pespconn->proto.udp->local_ip[1] = ip4_addr2_16(&upcb->local_ip); + precv->pespconn->proto.udp->local_ip[2] = ip4_addr3_16(&upcb->local_ip); + precv->pespconn->proto.udp->local_ip[3] = ip4_addr4_16(&upcb->local_ip); + + if (p != NULL) { +// q = p; + +// while (q != NULL) { +// pdata = (u8_t *)os_zalloc(q ->len + 1); +// length = pbuf_copy_partial(q, pdata, q ->len, 0); +// +// LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_server_recv %d %x\n", __LINE__, length)); +// precv->pcommon.pcb = upcb; +// +// if (length != 0) { +// if (precv->pespconn->recv_callback != NULL) { +// precv->pespconn->recv_callback(precv->pespconn, pdata, length); +// } +// } +// +// q = q->next; +// os_free(pdata); +// } + pdata = (u8_t *)os_zalloc(p ->tot_len + 1); + length = pbuf_copy_partial(p, pdata, p ->tot_len, 0); + precv->pcommon.pcb = upcb; + pbuf_free(p); + if (length != 0) { + if (precv->pespconn->recv_callback != NULL) { + precv->pespconn->recv_callback(precv->pespconn, pdata, length); + } + } + os_free(pdata); + } else { + return; + } +} + +/****************************************************************************** + * FunctionName : espconn_udp_disconnect + * Description : A new incoming connection has been disconnected. + * Parameters : espconn -- the espconn used to disconnect with host + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_udp_disconnect(espconn_msg *pdiscon) +{ + if (pdiscon == NULL) { + return; + } + + struct udp_pcb *upcb = pdiscon->pcommon.pcb; + + udp_disconnect(upcb); + + udp_remove(upcb); + + espconn_list_delete(&plink_active, pdiscon); + + os_free(pdiscon); + pdiscon = NULL; +} + +/****************************************************************************** + * FunctionName : espconn_udp_server + * Description : Initialize the server: set up a PCB and bind it to the port + * Parameters : pespconn -- the espconn used to build server + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_udp_server(struct espconn *pespconn) +{ + struct udp_pcb *upcb = NULL; + espconn_msg *pserver = NULL; + upcb = udp_new(); + + if (upcb == NULL) { + return ESPCONN_MEM; + } else { + pserver = (espconn_msg *)os_zalloc(sizeof(espconn_msg)); + + if (pserver == NULL) { + udp_remove(upcb); + return ESPCONN_MEM; + } + + pserver->pcommon.pcb = upcb; + pserver->pespconn = pespconn; + espconn_list_creat(&plink_active, pserver); + udp_bind(upcb, IP_ADDR_ANY, pserver->pespconn->proto.udp->local_port); + udp_recv(upcb, espconn_udp_recv, (void *)pserver); + return ESPCONN_OK; + } +} + +/****************************************************************************** + * FunctionName : espconn_igmp_leave + * Description : leave a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_igmp_leave(ip_addr_t *host_ip, ip_addr_t *multicast_ip) +{ + if (igmp_leavegroup(host_ip, multicast_ip) != ERR_OK) { + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("udp_leave_multigrup failed!\n")); + return -1; + }; + + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_igmp_join + * Description : join a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_igmp_join(ip_addr_t *host_ip, ip_addr_t *multicast_ip) +{ + if (igmp_joingroup(host_ip, multicast_ip) != ERR_OK) { + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("udp_join_multigrup failed!\n")); + return -1; + }; + + /* join to any IP address at the port */ + return ESPCONN_OK; +} diff --git a/app/lwip/app/netio.c b/app/lwip/app/netio.c new file mode 100644 index 00000000..4124b912 --- /dev/null +++ b/app/lwip/app/netio.c @@ -0,0 +1,365 @@ +/** + * @file + * MetIO Server + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ +#include "lwip/opt.h" + +#if LWIP_TCP +#include "lwip/tcp.h" + +/* + * This implements a netio server. + * The client sends a command word (4 bytes) then a data length word (4 bytes). + * If the command is "receive", the server is to consume "data length" bytes into + * a circular buffer until the first byte is non-zero, then it is to consume + * another command/data pair. + * If the command is "send", the server is to send "data length" bytes from a circular + * buffer with the first byte being zero, until "some time" (6 seconds in the + * current netio126.zip download) has passed and then send one final buffer with + * the first byte being non-zero. Then it is to consume another command/data pair. + */ + +/* See http://www.nwlab.net/art/netio/netio.html to get the netio tool */ + +/* implementation options */ +#define NETIO_BUF_SIZE (4 * 1024) +#define NETIO_USE_STATIC_BUF 0 + +/* NetIO server state definition */ +#define NETIO_STATE_WAIT_FOR_CMD 0 +#define NETIO_STATE_RECV_DATA 1 +#define NETIO_STATE_SEND_DATA 2 +#define NETIO_STATE_SEND_DATA_LAST 3 +#define NETIO_STATE_DONE 4 + +struct netio_state { + u32_t state; + u32_t cmd; + u32_t data_len; + u32_t cntr; + u8_t * buf_ptr; + u32_t buf_pos; + u32_t first_byte; + u32_t time_stamp; +}; + +/* NetIO command protocol definition */ +#define NETIO_CMD_QUIT 0 +#define NETIO_CMD_C2S 1 +#define NETIO_CMD_S2C 2 +#define NETIO_CMD_RES 3 + +static err_t netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); + +static void ICACHE_FLASH_ATTR +netio_close(void *arg, struct tcp_pcb *pcb) +{ + err_t err; + + struct netio_state *ns = arg; + ns->state = NETIO_STATE_DONE; + tcp_recv(pcb, NULL); + err = tcp_close(pcb); + + if (err != ERR_OK) { + /* closing failed, try again later */ + tcp_recv(pcb, netio_recv); + } else { + /* closing succeeded */ +#if NETIO_USE_STATIC_BUF != 1 + if(ns->buf_ptr != NULL){ + mem_free(ns->buf_ptr); + } +#endif + tcp_arg(pcb, NULL); + tcp_poll(pcb, NULL, 0); + tcp_sent(pcb, NULL); + if (arg != NULL) { + mem_free(arg); + } + } +} + +static err_t ICACHE_FLASH_ATTR +netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + struct netio_state *ns = arg; + u8_t * data_ptr; + u32_t data_cntr; + struct pbuf *q = p; + u16_t len; + + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + } + + if (err == ERR_OK && q != NULL) { + + while (q != NULL) { + data_cntr = q->len; + data_ptr = q->payload; + while (data_cntr--) { + if (ns->state == NETIO_STATE_DONE){ + netio_close(ns, pcb); + break; + } else if (ns->state == NETIO_STATE_WAIT_FOR_CMD) { + if (ns->cntr < 4) { + /* build up the CMD field */ + ns->cmd <<= 8; + ns->cmd |= *data_ptr++; + ns->cntr++; + } else if (ns->cntr < 8) { + /* build up the DATA field */ + ns->data_len <<= 8; + ns->data_len |= *data_ptr++; + ns->cntr++; + + if (ns->cntr == 8) { + /* now we have full command and data words */ + ns->cntr = 0; + ns->buf_pos = 0; + ns->buf_ptr[0] = 0; + if (ns->cmd == NETIO_CMD_C2S) { + ns->state = NETIO_STATE_RECV_DATA; + } else if (ns->cmd == NETIO_CMD_S2C) { + ns->state = NETIO_STATE_SEND_DATA; + /* start timer */ + ns->time_stamp = sys_now(); + /* send first round of data */ + + len = tcp_sndbuf(pcb); + len = LWIP_MIN(len, ns->data_len - ns->cntr); + len = LWIP_MIN(len, NETIO_BUF_SIZE - ns->buf_pos); + + do { + err = tcp_write(pcb, ns->buf_ptr + ns->buf_pos, len, TCP_WRITE_FLAG_COPY); + if (err == ERR_MEM) { + len /= 2; + } + } while ((err == ERR_MEM) && (len > 1)); + + ns->buf_pos += len; + ns->cntr += len; + + } else { + /* unrecognized command, punt */ + ns->cntr = 0; + ns->buf_pos = 0; + ns->buf_ptr[0] = 0; + netio_close(ns, pcb); + break; + } + } + } else { + /* in trouble... shouldn't be in this state! */ + } + + } else if (ns->state == NETIO_STATE_RECV_DATA) { + + if(ns->cntr == 0){ + /* save the first byte of this new round of data + * this will not match ns->buf_ptr[0] in the case that + * NETIO_BUF_SIZE is less than ns->data_len. + */ + ns->first_byte = *data_ptr; + } + + ns->buf_ptr[ns->buf_pos++] = *data_ptr++; + ns->cntr++; + + if (ns->buf_pos == NETIO_BUF_SIZE) { + /* circularize the buffer */ + ns->buf_pos = 0; + } + + if(ns->cntr == ns->data_len){ + ns->cntr = 0; + if (ns->first_byte != 0) { + /* if this last round did not start with 0, + * go look for another command */ + ns->state = NETIO_STATE_WAIT_FOR_CMD; + ns->data_len = 0; + ns->cmd = 0; + /* TODO LWIP_DEBUGF( print out some throughput calculation results... ); */ + } else { + /* stay here and wait on more data */ + } + } + + } else if (ns->state == NETIO_STATE_SEND_DATA + || ns->state == NETIO_STATE_SEND_DATA_LAST) { + /* I don't think this should happen... */ + } else { + /* done / quit */ + netio_close(ns, pcb); + break; + } /* end of ns->state condition */ + } /* end of while data still in this pbuf */ + + q = q->next; + } + + pbuf_free(p); + + } else { + + /* error or closed by other side */ + if (p != NULL) { + pbuf_free(p); + } + + /* close the connection */ + netio_close(ns, pcb); + + } + return ERR_OK; + +} + +static err_t ICACHE_FLASH_ATTR +netio_sent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct netio_state *ns = arg; + err_t err = ERR_OK; + + if (ns->cntr >= ns->data_len && ns->state == NETIO_STATE_SEND_DATA) { + /* done with this round of sending */ + ns->buf_pos = 0; + ns->cntr = 0; + + /* check if timer expired */ + if (sys_now() - ns->time_stamp > 600) { + ns->buf_ptr[0] = 1; + ns->state = NETIO_STATE_SEND_DATA_LAST; + } else { + ns->buf_ptr[0] = 0; + } + } + + if(ns->state == NETIO_STATE_SEND_DATA_LAST || ns->state == NETIO_STATE_SEND_DATA){ + len = tcp_sndbuf(pcb); + len = LWIP_MIN(len, ns->data_len - ns->cntr); + len = LWIP_MIN(len, NETIO_BUF_SIZE - ns->buf_pos); + + if(ns->cntr < ns->data_len){ + do { + err = tcp_write(pcb, ns->buf_ptr + ns->buf_pos, len, TCP_WRITE_FLAG_COPY); + if (err == ERR_MEM) { + len /= 2; + } + } while ((err == ERR_MEM) && (len > 1)); + + ns->buf_pos += len; + if(ns->buf_pos >= NETIO_BUF_SIZE){ + ns->buf_pos = 0; + } + + ns->cntr += len; + } + } + + if(ns->cntr >= ns->data_len && ns->state == NETIO_STATE_SEND_DATA_LAST){ + /* we have buffered up all our data to send this last round, go look for a command */ + ns->state = NETIO_STATE_WAIT_FOR_CMD; + ns->cntr = 0; + /* TODO LWIP_DEBUGF( print out some throughput calculation results... ); */ + } + + return ERR_OK; +} + +static err_t ICACHE_FLASH_ATTR +netio_poll(void *arg, struct tcp_pcb *pcb) +{ + struct netio_state * ns = arg; + if(ns->state == NETIO_STATE_SEND_DATA){ + + } else if(ns->state == NETIO_STATE_DONE){ + netio_close(ns, pcb); + } + + return ERR_OK; + +} + +#if NETIO_USE_STATIC_BUF == 1 +static u8_t netio_buf[NETIO_BUF_SIZE]; +#endif + +static err_t ICACHE_FLASH_ATTR +netio_accept(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct netio_state * ns; + + LWIP_UNUSED_ARG(err); + + ns = mem_malloc(sizeof(struct netio_state)); + + if(ns == NULL){ + return ERR_MEM; + } + + ns->state = NETIO_STATE_WAIT_FOR_CMD; + ns->data_len = 0; + ns->cmd = 0; + ns->cntr = 0; + ns->buf_pos = 0; +#if NETIO_USE_STATIC_BUF == 1 + ns->buf_ptr = netio_buf; +#else + ns->buf_ptr = mem_malloc(NETIO_BUF_SIZE); + + if(ns->buf_ptr == NULL){ + mem_free(ns); + return ERR_MEM; + } +#endif + + ns->buf_ptr[0] = 0; + + tcp_arg(pcb, ns); + tcp_sent(pcb, netio_sent); + tcp_recv(pcb, netio_recv); + tcp_poll(pcb, netio_poll, 4); /* every 2 seconds */ + return ERR_OK; +} + +void ICACHE_FLASH_ATTR netio_init(void) +{ + struct tcp_pcb *pcb; + + pcb = tcp_new(); + tcp_bind(pcb, IP_ADDR_ANY, 18767); + pcb = tcp_listen(pcb); + tcp_accept(pcb, netio_accept); +} + +#endif /* LWIP_TCP */ diff --git a/app/lwip/app/ping.c b/app/lwip/app/ping.c new file mode 100644 index 00000000..d353fa27 --- /dev/null +++ b/app/lwip/app/ping.c @@ -0,0 +1,322 @@ +/** + * @file + * Ping sender module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +/** + * This is an example of a "ping" sender (with raw API and socket API). + * It can be used as a start point to maintain opened a network connection, or + * like a network "watchdog" for your device. + * + */ + +/* + * copyright (c) 2010 - 2011 Espressif System + */ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/raw.h" +#include "lwip/icmp.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/inet_chksum.h" +#include "os_type.h" +#include "osapi.h" + +#include "lwip/app/ping.h" + +#if PING_USE_SOCKETS +#include "lwip/sockets.h" +#include "lwip/inet.h" +#endif /* PING_USE_SOCKETS */ + +/* ping variables */ +static u16_t ping_seq_num = 0; +static u32_t ping_time; + +static void ICACHE_FLASH_ATTR ping_timeout(void* arg) +{ + struct ping_msg *pingmsg = (struct ping_msg *)arg; + pingmsg->timeout_count ++; + if (pingmsg->ping_opt->recv_function == NULL){ + os_printf("ping timeout\n"); + } else { + struct ping_resp pingresp; + os_bzero(&pingresp, sizeof(struct ping_resp)); + pingresp.ping_err = -1; + pingmsg->ping_opt->recv_function(pingmsg->ping_opt, (void*)&pingresp); + } +} + +/** Prepare a echo ICMP request */ +static void ICACHE_FLASH_ATTR +ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len) +{ + size_t i = 0; + size_t data_len = len - sizeof(struct icmp_echo_hdr); + + ICMPH_TYPE_SET(iecho, ICMP_ECHO); + ICMPH_CODE_SET(iecho, 0); + iecho->chksum = 0; + iecho->id = PING_ID; + ++ ping_seq_num; + iecho->seqno = htons(ping_seq_num); + + /* fill the additional data buffer with some data */ + for(i = 0; i < data_len; i++) { + ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i; + } + + iecho->chksum = inet_chksum(iecho, len); +} + +static void ICACHE_FLASH_ATTR +ping_prepare_er(struct icmp_echo_hdr *iecho, u16_t len) +{ + + ICMPH_TYPE_SET(iecho, ICMP_ER); + ICMPH_CODE_SET(iecho, 0); + iecho->chksum = 0; + + iecho->chksum = inet_chksum(iecho, len); +} + +/* Ping using the raw ip */ +static u8_t ICACHE_FLASH_ATTR +ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *addr) +{ + struct icmp_echo_hdr *iecho = NULL; + static u16_t seqno = 0; + struct ping_msg *pingmsg = (struct ping_msg*)arg; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_ASSERT("p != NULL", p != NULL); + + if (pbuf_header( p, -PBUF_IP_HLEN)==0) { + iecho = (struct icmp_echo_hdr *)p->payload; + + if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num)) && iecho->type == ICMP_ER) { + LWIP_DEBUGF( PING_DEBUG, ("ping: recv ")); + ip_addr_debug_print(PING_DEBUG, addr); + LWIP_DEBUGF( PING_DEBUG, (" %"U32_F" ms\n", (sys_now()-ping_time))); + if (iecho->seqno != seqno){ + /* do some ping result processing */ + { + struct ip_hdr *iphdr = NULL; + char ipaddrstr[16]; + ip_addr_t source_ip; + sys_untimeout(ping_timeout, pingmsg); + os_bzero(&source_ip, sizeof(ip_addr_t)); + os_bzero(ipaddrstr, sizeof(ipaddrstr)); + uint32 delay = system_relative_time(pingmsg->ping_sent); + delay /= PING_COARSE; + iphdr = (struct ip_hdr*)((u8*)iecho - PBUF_IP_HLEN); + source_ip.addr = iphdr->src.addr; + ipaddr_ntoa_r(&source_ip,ipaddrstr, sizeof(ipaddrstr)); + if (pingmsg->ping_opt->recv_function == NULL){ + os_printf("recv %s: byte = %d, time = %d ms, seq = %d\n",ipaddrstr, PING_DATA_SIZE, delay, ntohs(iecho->seqno)); + } else { + struct ping_resp pingresp; + os_bzero(&pingresp, sizeof(struct ping_resp)); + pingresp.bytes = PING_DATA_SIZE; + pingresp.resp_time = delay; + pingresp.seqno = ntohs(iecho->seqno); + pingresp.ping_err = 0; + pingmsg->ping_opt->recv_function(pingmsg->ping_opt,(void*) &pingresp); + } + } + seqno = iecho->seqno; + } + + PING_RESULT(1); + pbuf_free(p); + return 1; /* eat the packet */ + } +// } else if(iecho->type == ICMP_ECHO){ +// struct pbuf *q = NULL; +// os_printf("receive ping request:seq=%d\n", ntohs(iecho->seqno)); +// q = pbuf_alloc(PBUF_IP, (u16_t)p->tot_len, PBUF_RAM); +// if (q!=NULL) { +// pbuf_copy(q, p); +// iecho = (struct icmp_echo_hdr *)q->payload; +// ping_prepare_er(iecho, q->tot_len); +// raw_sendto(pcb, q, addr); +// pbuf_free(q); +// } +// pbuf_free(p); +// return 1; +// } + } + + return 0; /* don't eat the packet */ +} + +static void ICACHE_FLASH_ATTR +ping_send(struct raw_pcb *raw, ip_addr_t *addr) +{ + struct pbuf *p = NULL; + struct icmp_echo_hdr *iecho = NULL; + size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE; + + LWIP_DEBUGF( PING_DEBUG, ("ping: send ")); + ip_addr_debug_print(PING_DEBUG, addr); + LWIP_DEBUGF( PING_DEBUG, ("\n")); + LWIP_ASSERT("ping_size <= 0xffff", ping_size <= 0xffff); + + p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM); + if (!p) { + return; + } + if ((p->len == p->tot_len) && (p->next == NULL)) { + iecho = (struct icmp_echo_hdr *)p->payload; + + ping_prepare_echo(iecho, (u16_t)ping_size); + + raw_sendto(raw, p, addr); + ping_time = sys_now(); + } + pbuf_free(p); +} + +static void ICACHE_FLASH_ATTR +ping_coarse_tmr(void *arg) +{ + struct ping_msg *pingmsg = (struct ping_msg*)arg; + struct ping_option *ping_opt= NULL; + struct ping_resp pingresp; + ip_addr_t ping_target; + + LWIP_ASSERT("ping_timeout: no pcb given!", pingmsg != NULL); + ping_target.addr = pingmsg->ping_opt->ip; + ping_opt = pingmsg->ping_opt; + if (--pingmsg->sent_count != 0){ + pingmsg ->ping_sent = system_get_time(); + ping_send(pingmsg->ping_pcb, &ping_target); + + sys_timeout(PING_TIMEOUT_MS, ping_timeout, pingmsg); + sys_timeout(pingmsg->coarse_time, ping_coarse_tmr, pingmsg); + } else { + uint32 delay = system_relative_time(pingmsg->ping_start); + delay /= PING_COARSE; + ping_seq_num = 0; + if (ping_opt->sent_function == NULL){ + os_printf("ping %d, timeout %d, total payload %d bytes, %d ms\n", + pingmsg->max_count, pingmsg->timeout_count, PING_DATA_SIZE*(pingmsg->max_count - pingmsg->timeout_count),delay); + } else { + os_bzero(&pingresp, sizeof(struct ping_resp)); + pingresp.total_count = pingmsg->max_count; + pingresp.timeout_count = pingmsg->timeout_count; + pingresp.total_bytes = PING_DATA_SIZE*(pingmsg->max_count - pingmsg->timeout_count); + pingresp.total_time = delay; + pingresp.ping_err = 0; + } + sys_untimeout(ping_coarse_tmr, pingmsg); + raw_remove(pingmsg->ping_pcb); + os_free(pingmsg); + if (ping_opt->sent_function != NULL) + ping_opt->sent_function(ping_opt,(uint8*)&pingresp); + } +} + +static bool ICACHE_FLASH_ATTR +ping_raw_init(struct ping_msg *pingmsg) +{ + if (pingmsg == NULL) + return false; + + ip_addr_t ping_target; + pingmsg->ping_pcb = raw_new(IP_PROTO_ICMP); + LWIP_ASSERT("ping_pcb != NULL", pingmsg->ping_pcb != NULL); + + raw_recv(pingmsg->ping_pcb, ping_recv, pingmsg); + raw_bind(pingmsg->ping_pcb, IP_ADDR_ANY); + + ping_target.addr = pingmsg->ping_opt->ip; + pingmsg ->ping_sent = system_get_time(); + ping_send(pingmsg->ping_pcb, &ping_target); + + sys_timeout(PING_TIMEOUT_MS, ping_timeout, pingmsg); + sys_timeout(pingmsg->coarse_time, ping_coarse_tmr, pingmsg); + return true; +} + +bool ICACHE_FLASH_ATTR +ping_start(struct ping_option *ping_opt) +{ + struct ping_msg *pingmsg = NULL; + pingmsg = (struct ping_msg *)os_zalloc(sizeof(struct ping_msg)); + if (pingmsg == NULL || ping_opt == NULL) + return false; + + pingmsg->ping_opt = ping_opt; + if (ping_opt->count != 0) + pingmsg->max_count = ping_opt->count; + else + pingmsg->max_count = DEFAULT_PING_MAX_COUNT; + + if (ping_opt->coarse_time != 0) + pingmsg->coarse_time = ping_opt->coarse_time * PING_COARSE; + else + pingmsg->coarse_time = PING_COARSE; + + pingmsg->ping_start = system_get_time(); + pingmsg->sent_count = pingmsg->max_count; + return ping_raw_init(pingmsg); +} + +bool ICACHE_FLASH_ATTR +ping_regist_recv(struct ping_option *ping_opt, ping_recv_function ping_recv) +{ + if (ping_opt == NULL) + return false; + + ping_opt ->recv_function = ping_recv; + return true; +} + +bool ICACHE_FLASH_ATTR +ping_regist_sent(struct ping_option *ping_opt, ping_sent_function ping_sent) +{ + if (ping_opt == NULL) + return false; + + ping_opt ->sent_function = ping_sent; + return true; +} + +#endif /* LWIP_RAW */ diff --git a/app/lwip/core/Makefile b/app/lwip/core/Makefile new file mode 100644 index 00000000..7cbb57b6 --- /dev/null +++ b/app/lwip/core/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = liblwipcore.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/lwip/core/def.c b/app/lwip/core/def.c new file mode 100644 index 00000000..352b5524 --- /dev/null +++ b/app/lwip/core/def.c @@ -0,0 +1,108 @@ +/** + * @file + * Common functions used throughout the stack. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/opt.h" +#include "lwip/def.h" + +/** + * These are reference implementations of the byte swapping functions. + * Again with the aim of being simple, correct and fully portable. + * Byte swapping is the second thing you would want to optimize. You will + * need to port it to your architecture and in your cc.h: + * + * #define LWIP_PLATFORM_BYTESWAP 1 + * #define LWIP_PLATFORM_HTONS(x) + * #define LWIP_PLATFORM_HTONL(x) + * + * Note ntohs() and ntohl() are merely references to the htonx counterparts. + */ + +#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) + +/** + * Convert an u16_t from host- to network byte order. + * + * @param n u16_t in host byte order + * @return n in network byte order + */ +u16_t +lwip_htons(u16_t n) +{ + return ((n & 0xff) << 8) | ((n & 0xff00) >> 8); +} + +/** + * Convert an u16_t from network- to host byte order. + * + * @param n u16_t in network byte order + * @return n in host byte order + */ +u16_t +lwip_ntohs(u16_t n) +{ + return lwip_htons(n); +} + +/** + * Convert an u32_t from host- to network byte order. + * + * @param n u32_t in host byte order + * @return n in network byte order + */ +u32_t +lwip_htonl(u32_t n) +{ + return ((n & 0xff) << 24) | + ((n & 0xff00) << 8) | + ((n & 0xff0000UL) >> 8) | + ((n & 0xff000000UL) >> 24); +} + +/** + * Convert an u32_t from network- to host byte order. + * + * @param n u32_t in network byte order + * @return n in host byte order + */ +u32_t +lwip_ntohl(u32_t n) +{ + return lwip_htonl(n); +} + +#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */ diff --git a/app/lwip/core/dhcp.c b/app/lwip/core/dhcp.c new file mode 100644 index 00000000..3b189f28 --- /dev/null +++ b/app/lwip/core/dhcp.c @@ -0,0 +1,1766 @@ +/** + * @file + * Dynamic Host Configuration Protocol client + * + */ + +/* + * + * Copyright (c) 2001-2004 Leon Woestenberg + * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. + * + * Author: Leon Woestenberg + * + * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform + * with RFC 2131 and RFC 2132. + * + * TODO: + * - Support for interfaces other than Ethernet (SLIP, PPP, ...) + * + * Please coordinate changes and requests with Leon Woestenberg + * + * + * Integration with your code: + * + * In lwip/dhcp.h + * #define DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute) + * #define DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer) + * + * Then have your application call dhcp_coarse_tmr() and + * dhcp_fine_tmr() on the defined intervals. + * + * dhcp_start(struct netif *netif); + * starts a DHCP client instance which configures the interface by + * obtaining an IP address lease and maintaining it. + * + * Use dhcp_release(netif) to end the lease and use dhcp_stop(netif) + * to remove the DHCP client. + * + */ + +#include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/def.h" +#include "lwip/sys.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/dns.h" +#include "netif/etharp.h" + +#include + +/** Default for DHCP_GLOBAL_XID is 0xABCD0000 + * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g. + * #define DHCP_GLOBAL_XID_HEADER "stdlib.h" + * #define DHCP_GLOBAL_XID rand() + */ +#ifdef DHCP_GLOBAL_XID_HEADER +#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */ +#endif + +/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU + * MTU is checked to be big enough in dhcp_start */ +#define DHCP_MAX_MSG_LEN(netif) (netif->mtu) +#define DHCP_MAX_MSG_LEN_MIN_REQUIRED 576 +/** Minimum length for reply before packet is parsed */ +#define DHCP_MIN_REPLY_LEN 44 + +#define REBOOT_TRIES 2 + +/** Option handling: options are parsed in dhcp_parse_reply + * and saved in an array where other functions can load them from. + * This might be moved into the struct dhcp (not necessarily since + * lwIP is single-threaded and the array is only used while in recv + * callback). */ +#define DHCP_OPTION_IDX_OVERLOAD 0 +#define DHCP_OPTION_IDX_MSG_TYPE 1 +#define DHCP_OPTION_IDX_SERVER_ID 2 +#define DHCP_OPTION_IDX_LEASE_TIME 3 +#define DHCP_OPTION_IDX_T1 4 +#define DHCP_OPTION_IDX_T2 5 +#define DHCP_OPTION_IDX_SUBNET_MASK 6 +#define DHCP_OPTION_IDX_ROUTER 7 +#define DHCP_OPTION_IDX_DNS_SERVER 8 +#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS) + +/** Holds the decoded option values, only valid while in dhcp_recv. + @todo: move this into struct dhcp? */ +u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX]; +/** Holds a flag which option was received and is contained in dhcp_rx_options_val, + only valid while in dhcp_recv. + @todo: move this into struct dhcp? */ +u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX]; + +#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0) +#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1) +#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0) +#define dhcp_clear_all_options(dhcp) (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given))) +#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx]) +#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val)) + + +/* DHCP client state machine functions */ +static err_t dhcp_discover(struct netif *netif); +static err_t dhcp_select(struct netif *netif); +static void dhcp_bind(struct netif *netif); +#if DHCP_DOES_ARP_CHECK +static err_t dhcp_decline(struct netif *netif); +#endif /* DHCP_DOES_ARP_CHECK */ +static err_t dhcp_rebind(struct netif *netif); +static err_t dhcp_reboot(struct netif *netif); +static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state); + +/* receive, unfold, parse and free incoming messages */ +static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); + +/* set the DHCP timers */ +static void dhcp_timeout(struct netif *netif); +static void dhcp_t1_timeout(struct netif *netif); +static void dhcp_t2_timeout(struct netif *netif); + +/* build outgoing messages */ +/* create a DHCP message, fill in common headers */ +static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type); +/* free a DHCP request */ +static void dhcp_delete_msg(struct dhcp *dhcp); +/* add a DHCP option (type, then length in bytes) */ +static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len); +/* add option values */ +static void dhcp_option_byte(struct dhcp *dhcp, u8_t value); +static void dhcp_option_short(struct dhcp *dhcp, u16_t value); +static void dhcp_option_long(struct dhcp *dhcp, u32_t value); +/* always add the DHCP options trailer to end and pad */ +static void dhcp_option_trailer(struct dhcp *dhcp); + +/** + * Back-off the DHCP client (because of a received NAK response). + * + * Back-off the DHCP client because of a received NAK. Receiving a + * NAK means the client asked for something non-sensible, for + * example when it tries to renew a lease obtained on another network. + * + * We clear any existing set IP address and restart DHCP negotiation + * afresh (as per RFC2131 3.2.3). + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_handle_nak(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* Set the interface down since the address must no longer be used, as per RFC2131 */ + netif_set_down(netif); + /* remove IP address from interface */ + netif_set_ipaddr(netif, IP_ADDR_ANY); + netif_set_gw(netif, IP_ADDR_ANY); + netif_set_netmask(netif, IP_ADDR_ANY); + /* Change to a defined state */ + dhcp_set_state(dhcp, DHCP_BACKING_OFF); + /* We can immediately restart discovery */ + dhcp_discover(netif); +} + +#if DHCP_DOES_ARP_CHECK +/** + * Checks if the offered IP address is already in use. + * + * It does so by sending an ARP request for the offered address and + * entering CHECKING state. If no ARP reply is received within a small + * interval, the address is assumed to be free for use by us. + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_check(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0], + (s16_t)netif->name[1])); + dhcp_set_state(dhcp, DHCP_CHECKING); + /* create an ARP query for the offered IP address, expecting that no host + responds, as the IP address should not be in use. */ + result = etharp_query(netif, &dhcp->offered_ip_addr, NULL); + if (result != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n")); + } + dhcp->tries++; + msecs = 500; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs)); +} +#endif /* DHCP_DOES_ARP_CHECK */ + +/** + * Remember the configuration offered by a DHCP server. + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_handle_offer(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* obtain the server address */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) { + ip4_addr_set_u32(&dhcp->server_ip_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID))); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->server_ip_addr))); + /* remember offered address */ + ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_select(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif)); + } +} + +/** + * Select a DHCP server offer out of all offers. + * + * Simply select the first offer received. + * + * @param netif the netif under DHCP control + * @return lwIP specific error (see error.h) + */ +static err_t ICACHE_FLASH_ATTR +dhcp_select(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + dhcp_set_state(dhcp, DHCP_REQUESTING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + + /* MUST request the offered IP address */ + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->server_ip_addr))); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/); + dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); + dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); + dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); + +#if LWIP_NETIF_HOSTNAME + if (netif->hostname != NULL) { + const char *p = (const char*)netif->hostname; + u8_t namelen = (u8_t)strlen(p); + if (namelen > 0) { + LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen); + while (*p) { + dhcp_option_byte(dhcp, *p++); + } + } + } +#endif /* LWIP_NETIF_HOSTNAME */ + + dhcp_option_trailer(dhcp); + /* shrink the pbuf to the actual content length */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* send broadcast to any DHCP server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * The DHCP timer that checks for lease renewal/rebind timeouts. + */ +void ICACHE_FLASH_ATTR +dhcp_coarse_tmr() +{ + struct netif *netif = netif_list; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n")); + /* iterate through all network interfaces */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + if (netif->dhcp != NULL) { + /* timer is active (non zero), and triggers (zeroes) now? */ + if (netif->dhcp->t2_timeout-- == 1) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n")); + /* this clients' rebind timeout triggered */ + dhcp_t2_timeout(netif); + /* timer is active (non zero), and triggers (zeroes) now */ + } else if (netif->dhcp->t1_timeout-- == 1) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n")); + /* this clients' renewal timeout triggered */ + dhcp_t1_timeout(netif); + } + } + /* proceed to next netif */ + netif = netif->next; + } +} + +/** + * DHCP transaction timeout handling + * + * A DHCP server is expected to respond within a short period of time. + * This timer checks whether an outstanding DHCP request is timed out. + */ +void ICACHE_FLASH_ATTR +dhcp_fine_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + if (netif->dhcp != NULL) { + /* timer is active (non zero), and is about to trigger now */ + if (netif->dhcp->request_timeout > 1) { + netif->dhcp->request_timeout--; + } + else if (netif->dhcp->request_timeout == 1) { + netif->dhcp->request_timeout--; + /* { netif->dhcp->request_timeout == 0 } */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n")); + /* this client's request timeout triggered */ + dhcp_timeout(netif); + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * A DHCP negotiation transaction, or ARP request, has timed out. + * + * The timer that was started with the DHCP or ARP request has + * timed out, indicating no response was received in time. + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n")); + /* back-off period has passed, or server selection timed out */ + if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n")); + dhcp_discover(netif); + /* receiving the requested lease timed out */ + } else if (dhcp->state == DHCP_REQUESTING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n")); + if (dhcp->tries <= 5) { + dhcp_select(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n")); + dhcp_release(netif); + dhcp_discover(netif); + } +#if DHCP_DOES_ARP_CHECK + /* received no ARP reply for the offered address (which is good) */ + } else if (dhcp->state == DHCP_CHECKING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n")); + if (dhcp->tries <= 1) { + dhcp_check(netif); + /* no ARP replies on the offered address, + looks like the IP address is indeed free */ + } else { + /* bind the interface to the offered address */ + dhcp_bind(netif); + } +#endif /* DHCP_DOES_ARP_CHECK */ + } + /* did not get response to renew request? */ + else if (dhcp->state == DHCP_RENEWING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n")); + /* just retry renewal */ + /* note that the rebind timer will eventually time-out if renew does not work */ + dhcp_renew(netif); + /* did not get response to rebind request? */ + } else if (dhcp->state == DHCP_REBINDING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n")); + if (dhcp->tries <= 8) { + dhcp_rebind(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n")); + dhcp_release(netif); + dhcp_discover(netif); + } + } else if (dhcp->state == DHCP_REBOOTING) { + if (dhcp->tries < REBOOT_TRIES) { + dhcp_reboot(netif); + } else { + dhcp_discover(netif); + } + } +} + +/** + * The renewal period has timed out. + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_t1_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n")); + if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || + (dhcp->state == DHCP_RENEWING)) { + /* just retry to renew - note that the rebind timer (t2) will + * eventually time-out if renew tries fail. */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("dhcp_t1_timeout(): must renew\n")); + /* This slightly different to RFC2131: DHCPREQUEST will be sent from state + DHCP_RENEWING, not DHCP_BOUND */ + dhcp_renew(netif); + } +} + +/** + * The rebind period has timed out. + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_t2_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n")); + if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || + (dhcp->state == DHCP_RENEWING)) { + /* just retry to rebind */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("dhcp_t2_timeout(): must rebind\n")); + /* This slightly different to RFC2131: DHCPREQUEST will be sent from state + DHCP_REBINDING, not DHCP_BOUND */ + dhcp_rebind(netif); + } +} + +/** + * Handle a DHCP ACK packet + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_handle_ack(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; +#if LWIP_DNS + u8_t n; +#endif /* LWIP_DNS */ + + /* clear options we might not get from the ACK */ + ip_addr_set_zero(&dhcp->offered_sn_mask); + ip_addr_set_zero(&dhcp->offered_gw_addr); +#if LWIP_DHCP_BOOTP_FILE + ip_addr_set_zero(&dhcp->offered_si_addr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* lease time given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) { + /* remember offered lease time */ + dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME); + } + /* renewal period given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) { + /* remember given renewal period */ + dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1); + } else { + /* calculate safe periods for renewal */ + dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2; + } + + /* renewal period given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) { + /* remember given rebind period */ + dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2); + } else { + /* calculate safe periods for rebinding */ + dhcp->offered_t2_rebind = dhcp->offered_t0_lease; + } + + /* (y)our internet address */ + ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr); + +#if LWIP_DHCP_BOOTP_FILE + /* copy boot server address, + boot file name copied in dhcp_parse_reply if not overloaded */ + ip_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* subnet mask given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) { + /* remember given subnet mask */ + ip4_addr_set_u32(&dhcp->offered_sn_mask, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK))); + dhcp->subnet_mask_given = 1; + } else { + dhcp->subnet_mask_given = 0; + } + + /* gateway router */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) { + ip4_addr_set_u32(&dhcp->offered_gw_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER))); + } + +#if LWIP_DNS + /* DNS servers */ + n = 0; + while(dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n) && (n < DNS_MAX_SERVERS)) { + ip_addr_t dns_addr; + ip4_addr_set_u32(&dns_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n))); + dns_setserver(n, &dns_addr); + n++; + } +#endif /* LWIP_DNS */ +} + +/** Set a statically allocated struct dhcp to work with. + * Using this prevents dhcp_start to allocate it using mem_malloc. + * + * @param netif the netif for which to set the struct dhcp + * @param dhcp (uninitialised) dhcp struct allocated by the application + */ +void ICACHE_FLASH_ATTR +dhcp_set_struct(struct netif *netif, struct dhcp *dhcp) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("dhcp != NULL", dhcp != NULL); + LWIP_ASSERT("netif already has a struct dhcp set", netif->dhcp == NULL); + + /* clear data structure */ + memset(dhcp, 0, sizeof(struct dhcp)); + /* dhcp_set_state(&dhcp, DHCP_OFF); */ + netif->dhcp = dhcp; +} + +/** Removes a struct dhcp from a netif. + * + * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the + * struct dhcp since the memory is passed back to the heap. + * + * @param netif the netif from which to remove the struct dhcp + */ +void ICACHE_FLASH_ATTR dhcp_cleanup(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + + if (netif->dhcp != NULL) { + mem_free(netif->dhcp); + netif->dhcp = NULL; + } +} + +/** + * Start DHCP negotiation for a network interface. + * + * If no DHCP client instance was attached to this interface, + * a new client is created first. If a DHCP client instance + * was already present, it restarts negotiation. + * + * @param netif The lwIP network interface + * @return lwIP error code + * - ERR_OK - No error + * - ERR_MEM - Out of memory + */ +err_t ICACHE_FLASH_ATTR +dhcp_start(struct netif *netif) +{ + struct dhcp *dhcp; + err_t result = ERR_OK; + + LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;); + dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* Remove the flag that says this netif is handled by DHCP, + it is set when we succeeded starting. */ + netif->flags &= ~NETIF_FLAG_DHCP; + + /* check hwtype of the netif */ + if ((netif->flags & NETIF_FLAG_ETHARP) == 0) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n")); + return ERR_ARG; + } + + /* check MTU of the netif */ + if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n")); + return ERR_MEM; + } + + /* no DHCP client attached yet? */ + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n")); + dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp)); + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n")); + return ERR_MEM; + } + /* store this dhcp client in the netif */ + netif->dhcp = dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp")); + /* already has DHCP client attached */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n")); + if (dhcp->pcb != NULL) { + udp_remove(dhcp->pcb); + } + LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL); + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL ); + } + + /* clear data structure */ + memset(dhcp, 0, sizeof(struct dhcp)); + /* dhcp_set_state(&dhcp, DHCP_OFF); */ + /* allocate UDP PCB */ + dhcp->pcb = udp_new(); + if (dhcp->pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n")); + return ERR_MEM; + } + dhcp->pcb->so_options |= SOF_BROADCAST; + /* set up local and remote port for the pcb */ + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + /* set up the recv callback and argument */ + udp_recv(dhcp->pcb, dhcp_recv, netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n")); + /* (re)start the DHCP negotiation */ + result = dhcp_discover(netif); + if (result != ERR_OK) { + /* free resources allocated above */ + dhcp_stop(netif); + return ERR_MEM; + } + /* Set the flag that says this netif is handled by DHCP. */ + netif->flags |= NETIF_FLAG_DHCP; + return result; +} + +/** + * Inform a DHCP server of our manual configuration. + * + * This informs DHCP servers of our fixed IP address configuration + * by sending an INFORM message. It does not involve DHCP address + * configuration, it is just here to be nice to the network. + * + * @param netif The lwIP network interface + */ +void ICACHE_FLASH_ATTR +dhcp_inform(struct netif *netif) +{ + struct dhcp dhcp; + err_t result = ERR_OK; + struct udp_pcb *pcb; + + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + memset(&dhcp, 0, sizeof(struct dhcp)); + dhcp_set_state(&dhcp, DHCP_INFORM); + + if ((netif->dhcp != NULL) && (netif->dhcp->pcb != NULL)) { + /* re-use existing pcb */ + pcb = netif->dhcp->pcb; + } else { + pcb = udp_new(); + if (pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not obtain pcb")); + return; + } + dhcp.pcb = pcb; + dhcp.pcb->so_options |= SOF_BROADCAST; + udp_bind(dhcp.pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n")); + } + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, &dhcp, DHCP_INFORM); + if (result == ERR_OK) { + dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif)); + + dhcp_option_trailer(&dhcp); + + pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n")); + udp_sendto_if(pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(&dhcp); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n")); + } + + if (dhcp.pcb != NULL) { + /* otherwise, the existing pcb was used */ + udp_remove(dhcp.pcb); + } +} + +/** Handle a possible change in the network configuration. + * + * This enters the REBOOTING state to verify that the currently bound + * address is still valid. + */ +void ICACHE_FLASH_ATTR +dhcp_network_changed(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + if (!dhcp) + return; + switch (dhcp->state) { + case DHCP_REBINDING: + case DHCP_RENEWING: + case DHCP_BOUND: + case DHCP_REBOOTING: + netif_set_down(netif); + dhcp->tries = 0; + dhcp_reboot(netif); + break; + case DHCP_OFF: + /* stay off */ + break; + default: + dhcp->tries = 0; +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + dhcp_discover(netif); + break; + } +} + +#if DHCP_DOES_ARP_CHECK +/** + * Match an ARP reply with the offered IP address. + * + * @param netif the network interface on which the reply was received + * @param addr The IP address we received a reply from + */ +void ICACHE_FLASH_ATTR dhcp_arp_reply(struct netif *netif, ip_addr_t *addr) +{ + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n")); + /* is a DHCP client doing an ARP check? */ + if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n", + ip4_addr_get_u32(addr))); + /* did a host respond with the address we + were offered by the DHCP server? */ + if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) { + /* we will not accept the offered address */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("dhcp_arp_reply(): arp reply matched with offered address, declining\n")); + dhcp_decline(netif); + } + } +} + +/** + * Decline an offered lease. + * + * Tell the DHCP server we do not accept the offered address. + * One reason to decline the lease is when we find out the address + * is already in use by another host (through ARP). + * + * @param netif the netif under DHCP control + */ +static err_t ICACHE_FLASH_ATTR +dhcp_decline(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n")); + dhcp_set_state(dhcp, DHCP_BACKING_OFF); + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option_trailer(dhcp); + /* resize pbuf to reflect true size of options */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* per section 4.4.4, broadcast DECLINE messages */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_decline: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = 10*1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} +#endif /* DHCP_DOES_ARP_CHECK */ + + +/** + * Start the DHCP process, discover a DHCP server. + * + * @param netif the netif under DHCP control + */ +static err_t ICACHE_FLASH_ATTR +dhcp_discover(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n")); + ip_addr_set_any(&dhcp->offered_ip_addr); + dhcp_set_state(dhcp, DHCP_SELECTING); + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER); + if (result == ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n")); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/); + dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); + dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); + dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); + + dhcp_option_trailer(dhcp); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n")); + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n")); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n")); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n")); + } + dhcp->tries++; +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) { + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON; + autoip_start(netif); + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** + * Bind the interface to the offered IP address. + * + * @param netif network interface to bind to the offered address + */ +static void ICACHE_FLASH_ATTR +dhcp_bind(struct netif *netif) +{ + u32_t timeout; + struct dhcp *dhcp; + ip_addr_t sn_mask, gw_addr; + LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + + /* temporary DHCP lease? */ + if (dhcp->offered_t1_renew != 0xffffffffUL) { + /* set renewal period timer */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew)); + timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if(timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t1_timeout = (u16_t)timeout; + if (dhcp->t1_timeout == 0) { + dhcp->t1_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000)); + } + /* set renewal period timer */ + if (dhcp->offered_t2_rebind != 0xffffffffUL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind)); + timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if(timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t2_timeout = (u16_t)timeout; + if (dhcp->t2_timeout == 0) { + dhcp->t2_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000)); + } + + /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. modify by ives at 2014.4.22*/ + if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) { + dhcp->t1_timeout = 0; + } + + if (dhcp->subnet_mask_given) { + /* copy offered network mask */ + ip_addr_copy(sn_mask, dhcp->offered_sn_mask); + } else { + /* subnet mask not given, choose a safe subnet mask given the network class */ + u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr); + if (first_octet <= 127) { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000)); + } else if (first_octet >= 192) { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00)); + } else { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000)); + } + } + + ip_addr_copy(gw_addr, dhcp->offered_gw_addr); + /* gateway address not given? */ + if (ip_addr_isany(&gw_addr)) { + /* copy network address */ + ip_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask); + /* use first host address on network as gateway */ + ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001)); + } + +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->offered_ip_addr))); + netif_set_ipaddr(netif, &dhcp->offered_ip_addr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n", + ip4_addr_get_u32(&sn_mask))); + netif_set_netmask(netif, &sn_mask); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n", + ip4_addr_get_u32(&gw_addr))); + + system_station_got_ip_set(&dhcp->offered_ip_addr, &sn_mask, &gw_addr); + + netif_set_gw(netif, &gw_addr); + /* bring the interface up */ + netif_set_up(netif); + /* netif is now bound to DHCP leased address */ + dhcp_set_state(dhcp, DHCP_BOUND); +} + +/** + * Renew an existing DHCP lease at the involved DHCP server. + * + * @param netif network interface which must renew its lease + */ +err_t ICACHE_FLASH_ATTR +dhcp_renew(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n")); + dhcp_set_state(dhcp, DHCP_RENEWING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + +#if LWIP_NETIF_HOSTNAME + if (netif->hostname != NULL) { + const char *p = (const char*)netif->hostname; + u8_t namelen = (u8_t)strlen(p); + if (namelen > 0) { + LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen); + while (*p) { + dhcp_option_byte(dhcp, *p++); + } + } + } +#endif /* LWIP_NETIF_HOSTNAME */ + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); +#endif + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); +#endif + /* append DHCP message trailer */ + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n")); + } + dhcp->tries++; + /* back-off on retries, but to a maximum of 20 seconds */ + msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Rebind with a DHCP server for an existing DHCP lease. + * + * @param netif network interface which must rebind with a DHCP server + */ +static err_t ICACHE_FLASH_ATTR +dhcp_rebind(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n")); + dhcp_set_state(dhcp, DHCP_REBINDING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + +#if LWIP_NETIF_HOSTNAME + if (netif->hostname != NULL) { + const char *p = (const char*)netif->hostname; + u8_t namelen = (u8_t)strlen(p); + if (namelen > 0) { + LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen); + while (*p) { + dhcp_option_byte(dhcp, *p++); + } + } + } +#endif /* LWIP_NETIF_HOSTNAME */ + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); +#endif + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* broadcast to server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Enter REBOOTING state to verify an existing lease + * + * @param netif network interface which must reboot + */ +static err_t ICACHE_FLASH_ATTR +dhcp_reboot(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n")); + dhcp_set_state(dhcp, DHCP_REBOOTING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* broadcast to server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** + * Release a DHCP lease. + * + * @param netif network interface which must release its lease + */ +err_t ICACHE_FLASH_ATTR +dhcp_release(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n")); + if (dhcp == NULL) { + return ERR_ARG; + } + + /* idle DHCP client */ + dhcp_set_state(dhcp, DHCP_OFF); + /* clean old DHCP offer */ + ip_addr_set_zero(&dhcp->server_ip_addr); + ip_addr_set_zero(&dhcp->offered_ip_addr); + ip_addr_set_zero(&dhcp->offered_sn_mask); + ip_addr_set_zero(&dhcp->offered_gw_addr); +#if LWIP_DHCP_BOOTP_FILE + ip_addr_set_zero(&dhcp->offered_si_addr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0; + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE); + if (result == ERR_OK) { + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs)); + /* bring the interface down */ + netif_set_down(netif); + /* remove IP address from interface */ + netif_set_ipaddr(netif, IP_ADDR_ANY); + netif_set_gw(netif, IP_ADDR_ANY); + netif_set_netmask(netif, IP_ADDR_ANY); + + return result; +} + +/** + * Remove the DHCP client from the interface. + * + * @param netif The network interface to stop DHCP on + */ +void ICACHE_FLASH_ATTR +dhcp_stop(struct netif *netif) +{ + struct dhcp *dhcp; + LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + /* Remove the flag that says this netif is handled by DHCP. */ + netif->flags &= ~NETIF_FLAG_DHCP; + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n")); + /* netif is DHCP configured? */ + if (dhcp != NULL) { +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + + if (dhcp->pcb != NULL) { + udp_remove(dhcp->pcb); + dhcp->pcb = NULL; + } + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL); + dhcp_set_state(dhcp, DHCP_OFF); + } +} + +/* + * Set the DHCP state of a DHCP client. + * + * If the state changed, reset the number of tries. + */ +static void ICACHE_FLASH_ATTR +dhcp_set_state(struct dhcp *dhcp, u8_t new_state) +{ + if (new_state != dhcp->state) { + dhcp->state = new_state; + dhcp->tries = 0; + dhcp->request_timeout = 0; + } +} + +/* + * Concatenate an option type and length field to the outgoing + * DHCP message. + * + */ +static void ICACHE_FLASH_ATTR +dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len) +{ + LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = option_type; + dhcp->msg_out->options[dhcp->options_out_len++] = option_len; +} +/* + * Concatenate a single byte to the outgoing DHCP message. + * + */ +static void ICACHE_FLASH_ATTR +dhcp_option_byte(struct dhcp *dhcp, u8_t value) +{ + LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = value; +} + +static void ICACHE_FLASH_ATTR +dhcp_option_short(struct dhcp *dhcp, u16_t value) +{ + LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU); +} + +static void ICACHE_FLASH_ATTR +dhcp_option_long(struct dhcp *dhcp, u32_t value) +{ + LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL)); +} + +/** + * Extract the DHCP message and the DHCP options. + * + * Extract the DHCP message and the DHCP options, each into a contiguous + * piece of memory. As a DHCP message is variable sized by its options, + * and also allows overriding some fields for options, the easy approach + * is to first unfold the options into a conitguous piece of memory, and + * use that further on. + * + */ +static err_t ICACHE_FLASH_ATTR +dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p) +{ + u8_t *options; + u16_t offset; + u16_t offset_max; + u16_t options_idx; + u16_t options_idx_max; + struct pbuf *q; + int parse_file_as_options = 0; + int parse_sname_as_options = 0; + + /* clear received options */ + dhcp_clear_all_options(dhcp); + /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */ + if (p->len < DHCP_SNAME_OFS) { + return ERR_BUF; + } + dhcp->msg_in = (struct dhcp_msg *)p->payload; +#if LWIP_DHCP_BOOTP_FILE + /* clear boot file name */ + dhcp->boot_file_name[0] = 0; +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* parse options */ + + /* start with options field */ + options_idx = DHCP_OPTIONS_OFS; + /* parse options to the end of the received packet */ + options_idx_max = p->tot_len; +again: + q = p; + while((q != NULL) && (options_idx >= q->len)) { + options_idx -= q->len; + options_idx_max -= q->len; + q = q->next; + } + if (q == NULL) { + return ERR_BUF; + } + offset = options_idx; + offset_max = options_idx_max; + options = (u8_t*)q->payload; + /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */ + while((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) { + u8_t op = options[offset]; + u8_t len; + u8_t decode_len = 0; + int decode_idx = -1; + u16_t val_offset = offset + 2; + /* len byte might be in the next pbuf */ + if (offset + 1 < q->len) { + len = options[offset + 1]; + } else { + len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0); + } + /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */ + decode_len = len; + switch(op) { + /* case(DHCP_OPTION_END): handled above */ + case(DHCP_OPTION_PAD): + /* special option: no len encoded */ + decode_len = len = 0; + /* will be increased below */ + offset--; + break; + case(DHCP_OPTION_SUBNET_MASK): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_SUBNET_MASK; + break; + case(DHCP_OPTION_ROUTER): + decode_len = 4; /* only copy the first given router */ + LWIP_ASSERT("len >= decode_len", len >= decode_len); + decode_idx = DHCP_OPTION_IDX_ROUTER; + break; + case(DHCP_OPTION_DNS_SERVER): + /* special case: there might be more than one server */ + LWIP_ASSERT("len % 4 == 0", len % 4 == 0); + /* limit number of DNS servers */ + decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS); + LWIP_ASSERT("len >= decode_len", len >= decode_len); + decode_idx = DHCP_OPTION_IDX_DNS_SERVER; + break; + case(DHCP_OPTION_LEASE_TIME): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_LEASE_TIME; + break; + case(DHCP_OPTION_OVERLOAD): + LWIP_ASSERT("len == 1", len == 1); + decode_idx = DHCP_OPTION_IDX_OVERLOAD; + break; + case(DHCP_OPTION_MESSAGE_TYPE): + LWIP_ASSERT("len == 1", len == 1); + decode_idx = DHCP_OPTION_IDX_MSG_TYPE; + break; + case(DHCP_OPTION_SERVER_ID): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_SERVER_ID; + break; + case(DHCP_OPTION_T1): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_T1; + break; + case(DHCP_OPTION_T2): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_T2; + break; + default: + decode_len = 0; + LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", op)); + break; + } + offset += len + 2; + if (decode_len > 0) { + u32_t value = 0; + u16_t copy_len; +decode_next: + LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX); + LWIP_ASSERT("option already decoded", !dhcp_option_given(dhcp, decode_idx)); + copy_len = LWIP_MIN(decode_len, 4); + pbuf_copy_partial(q, &value, copy_len, val_offset); + if (decode_len > 4) { + /* decode more than one u32_t */ + LWIP_ASSERT("decode_len % 4 == 0", decode_len % 4 == 0); + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, htonl(value)); + decode_len -= 4; + val_offset += 4; + decode_idx++; + goto decode_next; + } else if (decode_len == 4) { + value = ntohl(value); + } else { + LWIP_ASSERT("invalid decode_len", decode_len == 1); + value = ((u8_t*)&value)[0]; + } + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, value); + } + if (offset >= q->len) { + offset -= q->len; + offset_max -= q->len; + if ((offset < offset_max) && offset_max) { //modify by ives at 2014.4.22 + q = q->next; + LWIP_ASSERT("next pbuf was null", q); + options = (u8_t*)q->payload; + } else { + /* We've run out of bytes, probably no end marker. Don't proceed. */ + break; + } + } + } + /* is this an overloaded message? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) { + u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD); + dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD); + if (overload == DHCP_OVERLOAD_FILE) { + parse_file_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n")); + } else if (overload == DHCP_OVERLOAD_SNAME) { + parse_sname_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n")); + } else if (overload == DHCP_OVERLOAD_SNAME_FILE) { + parse_sname_as_options = 1; + parse_file_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload)); + } +#if LWIP_DHCP_BOOTP_FILE + if (!parse_file_as_options) { + /* only do this for ACK messages */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) && + (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK)) + /* copy bootp file name, don't care for sname (server hostname) */ + pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS); + /* make sure the string is really NULL-terminated */ + dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0; + } +#endif /* LWIP_DHCP_BOOTP_FILE */ + } + if (parse_file_as_options) { + /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */ + parse_file_as_options = 0; + options_idx = DHCP_FILE_OFS; + options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN; + goto again; + } else if (parse_sname_as_options) { + parse_sname_as_options = 0; + options_idx = DHCP_SNAME_OFS; + options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN; + goto again; + } + return ERR_OK; +} + +/** + * If an incoming DHCP message is in response to us, then trigger the state machine + */ +static void ICACHE_FLASH_ATTR +dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + struct netif *netif = (struct netif *)arg; + struct dhcp *dhcp = netif->dhcp; + struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload; + u8_t msg_type; + u8_t i; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p, + ip4_addr1_16(addr), ip4_addr2_16(addr), ip4_addr3_16(addr), ip4_addr4_16(addr), port)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len)); + /* prevent warnings about unused arguments */ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL); + + if (p->len < DHCP_MIN_REPLY_LEN) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n")); + goto free_pbuf_and_return; + } + + if (reply_msg->op != DHCP_BOOTREPLY) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op)); + goto free_pbuf_and_return; + } + /* iterate through hardware address and match against DHCP message */ + for (i = 0; i < netif->hwaddr_len; i++) { + if (netif->hwaddr[i] != reply_msg->chaddr[i]) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n", + (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i])); + goto free_pbuf_and_return; + } + } + /* match transaction ID against what we expected */ + if (ntohl(reply_msg->xid) != dhcp->xid) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",ntohl(reply_msg->xid),dhcp->xid)); + goto free_pbuf_and_return; + } + /* option fields could be unfold? */ + if (dhcp_parse_reply(dhcp, p) != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("problem unfolding DHCP message - too short on memory?\n")); + goto free_pbuf_and_return; + } + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n")); + /* obtain pointer to DHCP message type */ + if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n")); + goto free_pbuf_and_return; + } + + /* read DHCP message type */ + msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE); + /* message type is DHCP ACK? */ + if (msg_type == DHCP_ACK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n")); + /* in requesting state? */ + if (dhcp->state == DHCP_REQUESTING) { + dhcp_handle_ack(netif); +#if DHCP_DOES_ARP_CHECK + /* check if the acknowledged lease address is already in use */ + dhcp_check(netif); +#else + /* bind interface to the acknowledged lease address */ + dhcp_bind(netif); +#endif + } + /* already bound to the given lease address? */ + else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) { + dhcp_bind(netif); + } + } + /* received a DHCP_NAK in appropriate state? */ + else if ((msg_type == DHCP_NAK) && + ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) || + (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING ))) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n")); + dhcp_handle_nak(netif); + } + /* received a DHCP_OFFER in DHCP_SELECTING state? */ + else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_SELECTING state\n")); + dhcp->request_timeout = 0; + /* remember offered lease */ + dhcp_handle_offer(netif); + } +free_pbuf_and_return: + dhcp->msg_in = NULL; + pbuf_free(p); +} + +/** + * Create a DHCP request, fill in common headers + * + * @param netif the netif under DHCP control + * @param dhcp dhcp control struct + * @param message_type message type of the request + */ +static err_t ICACHE_FLASH_ATTR +dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type) +{ + u16_t i; +#ifndef DHCP_GLOBAL_XID + /** default global transaction identifier starting value (easy to match + * with a packet analyser). We simply increment for each new request. + * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one + * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */ + static u32_t xid = 0xABCD0000; +#else + static u32_t xid; + static u8_t xid_initialised = 0; + if (!xid_initialised) { + xid = DHCP_GLOBAL_XID; + xid_initialised = !xid_initialised; + } +#endif + LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;); + LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;); + LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL); + LWIP_ASSERT("dhcp_create_msg: dhcp->msg_out == NULL", dhcp->msg_out == NULL); + dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM); + if (dhcp->p_out == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_create_msg(): could not allocate pbuf\n")); + return ERR_MEM; + } + LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg", + (dhcp->p_out->len >= sizeof(struct dhcp_msg))); + + /* DHCP_REQUEST should reuse 'xid' from DHCPOFFER modify by ives at 2014.4.22*/ + if (message_type != DHCP_REQUEST) { + /* reuse transaction identifier in retransmissions */ + if (dhcp->tries == 0) { + xid++; + } + dhcp->xid = xid; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, + ("transaction id xid(%"X32_F")\n", xid)); + + dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload; + + dhcp->msg_out->op = DHCP_BOOTREQUEST; + /* TODO: make link layer independent */ + dhcp->msg_out->htype = DHCP_HTYPE_ETH; + dhcp->msg_out->hlen = netif->hwaddr_len; + dhcp->msg_out->hops = 0; + dhcp->msg_out->xid = htonl(dhcp->xid); + dhcp->msg_out->secs = 0; + /* we don't need the broadcast flag since we can receive unicast traffic + before being fully configured! */ + dhcp->msg_out->flags = 0; + ip_addr_set_zero(&dhcp->msg_out->ciaddr); + /* set ciaddr to netif->ip_addr based on message_type and state */ + if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) || + ((message_type == DHCP_REQUEST) && /* DHCP_BOUND not used for sending! */ + ((dhcp->state==DHCP_RENEWING) || dhcp->state==DHCP_REBINDING))) { + ip_addr_copy(dhcp->msg_out->ciaddr, netif->ip_addr); + } + ip_addr_set_zero(&dhcp->msg_out->yiaddr); + ip_addr_set_zero(&dhcp->msg_out->siaddr); + ip_addr_set_zero(&dhcp->msg_out->giaddr); + for (i = 0; i < DHCP_CHADDR_LEN; i++) { + /* copy netif hardware address, pad with zeroes */ + dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN) ? netif->hwaddr[i] : 0/* pad byte*/; + } + for (i = 0; i < DHCP_SNAME_LEN; i++) { + dhcp->msg_out->sname[i] = 0; + } + for (i = 0; i < DHCP_FILE_LEN; i++) { + dhcp->msg_out->file[i] = 0; + } + dhcp->msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE); + dhcp->options_out_len = 0; + /* fill options field with an incrementing array (for debugging purposes) */ + for (i = 0; i < DHCP_OPTIONS_LEN; i++) { + dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */ + } + /* Add option MESSAGE_TYPE */ + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, message_type); + return ERR_OK; +} + +/** + * Free previously allocated memory used to send a DHCP request. + * + * @param dhcp the dhcp struct to free the request from + */ +static void ICACHE_FLASH_ATTR +dhcp_delete_msg(struct dhcp *dhcp) +{ + LWIP_ERROR("dhcp_delete_msg: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_delete_msg: dhcp->p_out != NULL", dhcp->p_out != NULL); + LWIP_ASSERT("dhcp_delete_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL); + if (dhcp->p_out != NULL) { + pbuf_free(dhcp->p_out); + } + dhcp->p_out = NULL; + dhcp->msg_out = NULL; +} + +/** + * Add a DHCP message trailer + * + * Adds the END option to the DHCP message, and if + * necessary, up to three padding bytes. + * + * @param dhcp DHCP state structure + */ +static void ICACHE_FLASH_ATTR +dhcp_option_trailer(struct dhcp *dhcp) +{ + LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL); + LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END; + /* packet is too small, or not 4 byte aligned? */ + while (((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) && + (dhcp->options_out_len < DHCP_OPTIONS_LEN)) { + /* LWIP_DEBUGF(DHCP_DEBUG,("dhcp_option_trailer:dhcp->options_out_len=%"U16_F", DHCP_OPTIONS_LEN=%"U16_F, dhcp->options_out_len, DHCP_OPTIONS_LEN)); */ + LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); + /* add a fill/padding byte */ + dhcp->msg_out->options[dhcp->options_out_len++] = 0; + } +} + +#endif /* LWIP_DHCP */ diff --git a/app/lwip/core/dns.c b/app/lwip/core/dns.c new file mode 100644 index 00000000..ab6821f2 --- /dev/null +++ b/app/lwip/core/dns.c @@ -0,0 +1,970 @@ +/** + * @file + * DNS - host name to IP address resolver. + * + */ + +/** + + * This file implements a DNS host name to IP address resolver. + + * Port to lwIP from uIP + * by Jim Pettinato April 2007 + + * uIP version Copyright (c) 2002-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DNS.C + * + * The lwIP DNS resolver functions are used to lookup a host name and + * map it to a numerical IP address. It maintains a list of resolved + * hostnames that can be queried with the dns_lookup() function. + * New hostnames can be resolved using the dns_query() function. + * + * The lwIP version of the resolver also adds a non-blocking version of + * gethostbyname() that will work with a raw API application. This function + * checks for an IP address string first and converts it if it is valid. + * gethostbyname() then does a dns_lookup() to see if the name is + * already in the table. If so, the IP is returned. If not, a query is + * issued and the function returns with a ERR_INPROGRESS status. The app + * using the dns client must then go into a waiting state. + * + * Once a hostname has been resolved (or found to be non-existent), + * the resolver code calls a specified callback function (which + * must be implemented by the module that uses the resolver). + */ + +/*----------------------------------------------------------------------------- + * RFC 1035 - Domain names - implementation and specification + * RFC 2181 - Clarifications to the DNS Specification + *----------------------------------------------------------------------------*/ + +/** @todo: define good default values (rfc compliance) */ +/** @todo: improve answer parsing, more checkings... */ +/** @todo: check RFC1035 - 7.3. Processing responses */ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/dns.h" + +#include + +/** DNS server IP address */ +#ifndef DNS_SERVER_ADDRESS +#define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */ +#endif + +/** DNS server port address */ +#ifndef DNS_SERVER_PORT +#define DNS_SERVER_PORT 53 +#endif + +/** DNS maximum number of retries when asking for a name, before "timeout". */ +#ifndef DNS_MAX_RETRIES +#define DNS_MAX_RETRIES 4 +#endif + +/** DNS resource record max. TTL (one week as default) */ +#ifndef DNS_MAX_TTL +#define DNS_MAX_TTL 604800 +#endif + +/* DNS protocol flags */ +#define DNS_FLAG1_RESPONSE 0x80 +#define DNS_FLAG1_OPCODE_STATUS 0x10 +#define DNS_FLAG1_OPCODE_INVERSE 0x08 +#define DNS_FLAG1_OPCODE_STANDARD 0x00 +#define DNS_FLAG1_AUTHORATIVE 0x04 +#define DNS_FLAG1_TRUNC 0x02 +#define DNS_FLAG1_RD 0x01 +#define DNS_FLAG2_RA 0x80 +#define DNS_FLAG2_ERR_MASK 0x0f +#define DNS_FLAG2_ERR_NONE 0x00 +#define DNS_FLAG2_ERR_NAME 0x03 + +/* DNS protocol states */ +#define DNS_STATE_UNUSED 0 +#define DNS_STATE_NEW 1 +#define DNS_STATE_ASKING 2 +#define DNS_STATE_DONE 3 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS message header */ +struct dns_hdr { + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u8_t flags1); + PACK_STRUCT_FIELD(u8_t flags2); + PACK_STRUCT_FIELD(u16_t numquestions); + PACK_STRUCT_FIELD(u16_t numanswers); + PACK_STRUCT_FIELD(u16_t numauthrr); + PACK_STRUCT_FIELD(u16_t numextrarr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define SIZEOF_DNS_HDR 12 + +/** DNS query message structure. + No packing needed: only used locally on the stack. */ +struct dns_query { + /* DNS query record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + u16_t type; + u16_t cls; +}; +#define SIZEOF_DNS_QUERY 4 + +/** DNS answer message structure. + No packing needed: only used locally on the stack. */ +struct dns_answer { + /* DNS answer record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + u16_t type; + u16_t cls; + u32_t ttl; + u16_t len; +}; +#define SIZEOF_DNS_ANSWER 10 + +/** DNS table entry */ +struct dns_table_entry { + u8_t state; + u8_t numdns; + u8_t tmr; + u8_t retries; + u8_t seqno; + u8_t err; + u32_t ttl; + char name[DNS_MAX_NAME_LENGTH]; + ip_addr_t ipaddr; + /* pointer to callback on DNS query done */ + dns_found_callback found; + void *arg; +}; + +#if DNS_LOCAL_HOSTLIST + +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +/** Local host-list. For hostnames in this list, no + * external name resolution is performed */ +static struct local_hostlist_entry *local_hostlist_dynamic; +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/** Defining this allows the local_hostlist_static to be placed in a different + * linker section (e.g. FLASH) */ +#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE +#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static +#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */ +/** Defining this allows the local_hostlist_static to be placed in a different + * linker section (e.g. FLASH) */ +#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST +#define DNS_LOCAL_HOSTLIST_STORAGE_POST +#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */ +DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[] + DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT; + +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +static void dns_init_local(); +#endif /* DNS_LOCAL_HOSTLIST */ + + +/* forward declarations */ +static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); +static void dns_check_entries(void); + +/*----------------------------------------------------------------------------- + * Globales + *----------------------------------------------------------------------------*/ + +/* DNS variables */ +static struct udp_pcb *dns_pcb; +static u8_t dns_seqno; +static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; +static ip_addr_t dns_servers[DNS_MAX_SERVERS]; +/** Contiguous buffer for processing responses */ +static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)]; +static u8_t* dns_payload; + +/** + * Initialize the resolver: set up the UDP pcb and configure the default server + * (DNS_SERVER_ADDRESS). + */ +void ICACHE_FLASH_ATTR +dns_init() +{ + ip_addr_t dnsserver; + + dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer); + + /* initialize default DNS server address */ + DNS_SERVER_ADDRESS(&dnsserver); + + LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); + + /* if dns client not yet initialized... */ + if (dns_pcb == NULL) { + dns_pcb = udp_new(); + + if (dns_pcb != NULL) { + /* initialize DNS table not needed (initialized to zero since it is a + * global variable) */ + LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", + DNS_STATE_UNUSED == 0); + + /* initialize DNS client */ + udp_bind(dns_pcb, IP_ADDR_ANY, 0); + udp_recv(dns_pcb, dns_recv, NULL); + + /* initialize default DNS primary server */ + dns_setserver(0, &dnsserver); + } + } +#if DNS_LOCAL_HOSTLIST + dns_init_local(); +#endif +} + +/** + * Initialize one of the DNS servers. + * + * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS + * @param dnsserver IP address of the DNS server to set + */ +void ICACHE_FLASH_ATTR +dns_setserver(u8_t numdns, ip_addr_t *dnsserver) +{ + if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) && + (dnsserver != NULL) && !ip_addr_isany(dnsserver)) { + dns_servers[numdns] = (*dnsserver); + } +} + +/** + * Obtain one of the currently configured DNS server. + * + * @param numdns the index of the DNS server + * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS + * server has not been configured. + */ +ip_addr_t ICACHE_FLASH_ATTR +dns_getserver(u8_t numdns) +{ + if (numdns < DNS_MAX_SERVERS) { + return dns_servers[numdns]; + } else { + return *IP_ADDR_ANY; + } +} + +/** + * The DNS resolver client timer - handle retries and timeouts and should + * be called every DNS_TMR_INTERVAL milliseconds (every second by default). + */ +void +dns_tmr(void) +{ + if (dns_pcb != NULL) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n")); + dns_check_entries(); + } +} + +#if DNS_LOCAL_HOSTLIST +static void ICACHE_FLASH_ATTR +dns_init_local() +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) + int i; + struct local_hostlist_entry *entry; + /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */ + struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT; + size_t namelen; + for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) { + struct local_hostlist_entry *init_entry = &local_hostlist_init[i]; + LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL); + namelen = strlen(init_entry->name); + LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); + entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); + LWIP_ASSERT("mem-error in dns_init_local", entry != NULL); + if (entry != NULL) { + entry->name = (char*)entry + sizeof(struct local_hostlist_entry); + MEMCPY((char*)entry->name, init_entry->name, namelen); + ((char*)entry->name)[namelen] = 0; + entry->addr = init_entry->addr; + entry->next = local_hostlist_dynamic; + local_hostlist_dynamic = entry; + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */ +} + +/** + * Scans the local host-list for a hostname. + * + * @param hostname Hostname to look for in the local host-list + * @return The first IP address for the hostname in the local host-list or + * IPADDR_NONE if not found. + */ +static u32_t ICACHE_FLASH_ATTR +dns_lookup_local(const char *hostname) +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC + struct local_hostlist_entry *entry = local_hostlist_dynamic; + while(entry != NULL) { + if(strcmp(entry->name, hostname) == 0) { + return ip4_addr_get_u32(&entry->addr); + } + entry = entry->next; + } +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + int i; + for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) { + if(strcmp(local_hostlist_static[i].name, hostname) == 0) { + return ip4_addr_get_u32(&local_hostlist_static[i].addr); + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + return IPADDR_NONE; +} + +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +/** Remove all entries from the local host-list for a specific hostname + * and/or IP addess + * + * @param hostname hostname for which entries shall be removed from the local + * host-list + * @param addr address for which entries shall be removed from the local host-list + * @return the number of removed entries + */ +int ICACHE_FLASH_ATTR +dns_local_removehost(const char *hostname, const ip_addr_t *addr) +{ + int removed = 0; + struct local_hostlist_entry *entry = local_hostlist_dynamic; + struct local_hostlist_entry *last_entry = NULL; + while (entry != NULL) { + if (((hostname == NULL) || !strcmp(entry->name, hostname)) && + ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) { + struct local_hostlist_entry *free_entry; + if (last_entry != NULL) { + last_entry->next = entry->next; + } else { + local_hostlist_dynamic = entry->next; + } + free_entry = entry; + entry = entry->next; + memp_free(MEMP_LOCALHOSTLIST, free_entry); + removed++; + } else { + last_entry = entry; + entry = entry->next; + } + } + return removed; +} + +/** + * Add a hostname/IP address pair to the local host-list. + * Duplicates are not checked. + * + * @param hostname hostname of the new entry + * @param addr IP address of the new entry + * @return ERR_OK if succeeded or ERR_MEM on memory error + */ +err_t ICACHE_FLASH_ATTR +dns_local_addhost(const char *hostname, const ip_addr_t *addr) +{ + struct local_hostlist_entry *entry; + size_t namelen; + LWIP_ASSERT("invalid host name (NULL)", hostname != NULL); + namelen = strlen(hostname); + LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); + entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); + if (entry == NULL) { + return ERR_MEM; + } + entry->name = (char*)entry + sizeof(struct local_hostlist_entry); + MEMCPY((char*)entry->name, hostname, namelen); + ((char*)entry->name)[namelen] = 0; + ip_addr_copy(entry->addr, *addr); + entry->next = local_hostlist_dynamic; + local_hostlist_dynamic = entry; + return ERR_OK; +} +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/ +#endif /* DNS_LOCAL_HOSTLIST */ + +/** + * Look up a hostname in the array of known hostnames. + * + * @note This function only looks in the internal array of known + * hostnames, it does not send out a query for the hostname if none + * was found. The function dns_enqueue() can be used to send a query + * for a hostname. + * + * @param name the hostname to look up + * @return the hostname's IP address, as u32_t (instead of ip_addr_t to + * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname + * was not found in the cached dns_table. + */ +static u32_t ICACHE_FLASH_ATTR +dns_lookup(const char *name) +{ + u8_t i; +#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) + u32_t addr; +#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */ +#if DNS_LOCAL_HOSTLIST + if ((addr = dns_lookup_local(name)) != IPADDR_NONE) { + return addr; + } +#endif /* DNS_LOCAL_HOSTLIST */ +#ifdef DNS_LOOKUP_LOCAL_EXTERN + if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) { + return addr; + } +#endif /* DNS_LOOKUP_LOCAL_EXTERN */ + + /* Walk through name list, return entry if found. If not, return NULL. */ + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + if ((dns_table[i].state == DNS_STATE_DONE) && + (strcmp(name, dns_table[i].name) == 0)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); + ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr)); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + return ip4_addr_get_u32(&dns_table[i].ipaddr); + } + } + + return IPADDR_NONE; +} + +#if DNS_DOES_NAME_CHECK +/** + * Compare the "dotted" name "query" with the encoded name "response" + * to make sure an answer from the DNS server matches the current dns_table + * entry (otherwise, answers might arrive late for hostname not on the list + * any more). + * + * @param query hostname (not encoded) from the dns_table + * @param response encoded hostname in the DNS response + * @return 0: names equal; 1: names differ + */ +static u8_t ICACHE_FLASH_ATTR +dns_compare_name(unsigned char *query, unsigned char *response) +{ + unsigned char n; + + do { + n = *response++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + if ((*query) != (*response)) { + return 1; + } + ++response; + ++query; + --n; + }; + ++query; + } + } while (*response != 0); + + return 0; +} +#endif /* DNS_DOES_NAME_CHECK */ + +/** + * Walk through a compact encoded DNS name and return the end of the name. + * + * @param query encoded DNS name in the DNS server response + * @return end of the name + */ +static unsigned char * ICACHE_FLASH_ATTR +dns_parse_name(unsigned char *query) +{ + unsigned char n; + + do { + n = *query++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + ++query; + --n; + }; + } + } while (*query != 0); + + return query + 1; +} + +/** + * Send a DNS query packet. + * + * @param numdns index of the DNS server in the dns_servers table + * @param name hostname to query + * @param id index of the hostname in dns_table, used as transaction ID in the + * DNS query packet + * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise + */ +static err_t ICACHE_FLASH_ATTR +dns_send(u8_t numdns, const char* name, u8_t id) +{ + err_t err; + struct dns_hdr *hdr; + struct dns_query qry; + struct pbuf *p; + char *query, *nptr; + const char *pHostname; + u8_t n; + + LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", + (u16_t)(numdns), name)); + LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS); + LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns])); + + /* if here, we have either a new query or a retry on a previous query to process */ + p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH + + SIZEOF_DNS_QUERY, PBUF_RAM); + if (p != NULL) { + LWIP_ASSERT("pbuf must be in one piece", p->next == NULL); + /* fill dns header */ + hdr = (struct dns_hdr*)p->payload; + memset(hdr, 0, SIZEOF_DNS_HDR); + hdr->id = htons(id); + hdr->flags1 = DNS_FLAG1_RD; + hdr->numquestions = PP_HTONS(1); + query = (char*)hdr + SIZEOF_DNS_HDR; + pHostname = name; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while(*pHostname != 0); + *query++='\0'; + + /* fill dns query */ + qry.type = PP_HTONS(DNS_RRTYPE_A); + qry.cls = PP_HTONS(DNS_RRCLASS_IN); + SMEMCPY(query, &qry, SIZEOF_DNS_QUERY); + + /* resize pbuf to the exact dns query */ + pbuf_realloc(p, (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload)))); + + /* connect to the server for faster receiving */ + udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT); + /* send dns packet */ + err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT); + + /* free pbuf */ + pbuf_free(p); + } else { + err = ERR_MEM; + } + + return err; +} + +/** + * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query. + * Check an entry in the dns_table: + * - send out query for new entries + * - retry old pending entries on timeout (also with different servers) + * - remove completed entries from the table if their TTL has expired + * + * @param i index of the dns_table entry to check + */ +static void ICACHE_FLASH_ATTR +dns_check_entry(u8_t i) +{ + err_t err; + struct dns_table_entry *pEntry = &dns_table[i]; + + LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); + + switch(pEntry->state) { + + case DNS_STATE_NEW: { + /* initialize new entry */ + pEntry->state = DNS_STATE_ASKING; + pEntry->numdns = 0; + pEntry->tmr = 1; + pEntry->retries = 0; + + /* send DNS packet for this entry */ + err = dns_send(pEntry->numdns, pEntry->name, i); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("dns_send returned error: %s\n", lwip_strerr(err))); + } + break; + } + + case DNS_STATE_ASKING: { + if (--pEntry->tmr == 0) { + if (++pEntry->retries == DNS_MAX_RETRIES) { + if ((pEntry->numdns+1numdns+1])) { + /* change of server */ + pEntry->numdns++; + pEntry->tmr = 1; + pEntry->retries = 0; + break; + } else { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name)); + /* call specified callback function if provided */ + if (pEntry->found) + (*pEntry->found)(pEntry->name, NULL, pEntry->arg); + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + break; + } + } + + /* wait longer for the next retry */ + pEntry->tmr = pEntry->retries; + + /* send DNS packet for this entry */ + err = dns_send(pEntry->numdns, pEntry->name, i); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("dns_send returned error: %s\n", lwip_strerr(err))); + } + } + break; + } + + case DNS_STATE_DONE: { + /* if the time to live is nul */ + if (--pEntry->ttl == 0) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name)); + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + } + break; + } + case DNS_STATE_UNUSED: + /* nothing to do */ + break; + default: + LWIP_ASSERT("unknown dns_table entry state:", 0); + break; + } +} + +/** + * Call dns_check_entry for each entry in dns_table - check all entries. + */ +static void ICACHE_FLASH_ATTR +dns_check_entries(void) +{ + u8_t i; + + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + dns_check_entry(i); + } +} + +/** + * Receive input function for DNS response packets arriving for the dns UDP pcb. + * + * @params see udp.h + */ +static void ICACHE_FLASH_ATTR +dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + u16_t i; + char *pHostname; + struct dns_hdr *hdr; + struct dns_answer ans; + struct dns_table_entry *pEntry; + u16_t nquestions, nanswers; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + /* is the dns message too big ? */ + if (p->tot_len > DNS_MSG_SIZE) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n")); + /* free pbuf and return */ + goto memerr; + } + + /* is the dns message big enough ? */ + if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n")); + /* free pbuf and return */ + goto memerr; + } + + /* copy dns payload inside static buffer for processing */ + if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) { + /* The ID in the DNS header should be our entry into the name table. */ + hdr = (struct dns_hdr*)dns_payload; + i = htons(hdr->id); + if (i < DNS_TABLE_SIZE) { + pEntry = &dns_table[i]; + if(pEntry->state == DNS_STATE_ASKING) { + /* This entry is now completed. */ + pEntry->state = DNS_STATE_DONE; + pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK; + + /* We only care about the question(s) and the answers. The authrr + and the extrarr are simply discarded. */ + nquestions = htons(hdr->numquestions); + nanswers = htons(hdr->numanswers); + + /* Check for error. If so, call callback to inform. */ + if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + +#if DNS_DOES_NAME_CHECK + /* Check if the name in the "question" part match with the name in the entry. */ + if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } +#endif /* DNS_DOES_NAME_CHECK */ + + /* Skip the name in the "question" part */ + pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY; + + while (nanswers > 0) { + /* skip answer resource record's host name */ + pHostname = (char *) dns_parse_name((unsigned char *)pHostname); + + /* Check for IP address type and Internet class. Others are discarded. */ + SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER); + if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) && + (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) { + /* read the answer resource record's TTL, and maximize it if needed */ + pEntry->ttl = ntohl(ans.ttl); + if (pEntry->ttl > DNS_MAX_TTL) { + pEntry->ttl = DNS_MAX_TTL; + } + /* read the IP address after answer resource record's header */ + SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t)); + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name)); + ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr))); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + /* call specified callback function if provided */ + if (pEntry->found) { + (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg); + } + /* deallocate memory and return */ + goto memerr; + } else { + pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len); + } + --nanswers; + } + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + } + } + + /* deallocate memory and return */ + goto memerr; + +responseerr: + /* ERROR: call specified callback function with NULL as name to indicate an error */ + if (pEntry->found) { + (*pEntry->found)(pEntry->name, NULL, pEntry->arg); + } + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + +memerr: + /* free pbuf */ + pbuf_free(p); + return; +} + +/** + * Queues a new hostname to resolve and sends out a DNS query for that hostname + * + * @param name the hostname that is to be queried + * @param found a callback founction to be called on success, failure or timeout + * @param callback_arg argument to pass to the callback function + * @return @return a err_t return code. + */ +static err_t ICACHE_FLASH_ATTR +dns_enqueue(const char *name, dns_found_callback found, void *callback_arg) +{ + u8_t i; + u8_t lseq, lseqi; + struct dns_table_entry *pEntry = NULL; + size_t namelen; + + /* search an unused entry, or the oldest one */ + lseq = lseqi = 0; + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + pEntry = &dns_table[i]; + /* is it an unused entry ? */ + if (pEntry->state == DNS_STATE_UNUSED) + break; + + /* check if this is the oldest completed entry */ + if (pEntry->state == DNS_STATE_DONE) { + if ((dns_seqno - pEntry->seqno) > lseq) { + lseq = dns_seqno - pEntry->seqno; + lseqi = i; + } + } + } + + /* if we don't have found an unused entry, use the oldest completed one */ + if (i == DNS_TABLE_SIZE) { + if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) { + /* no entry can't be used now, table is full */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name)); + return ERR_MEM; + } else { + /* use the oldest completed one */ + i = lseqi; + pEntry = &dns_table[i]; + } + } + + /* use this entry */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); + + /* fill the entry */ + pEntry->state = DNS_STATE_NEW; + pEntry->seqno = dns_seqno++; + pEntry->found = found; + pEntry->arg = callback_arg; + namelen = LWIP_MIN(strlen(name), DNS_MAX_NAME_LENGTH-1); + MEMCPY(pEntry->name, name, namelen); + pEntry->name[namelen] = 0; + + /* force to send query without waiting timer */ + dns_check_entry(i); + + /* dns query is enqueued */ + return ERR_INPROGRESS; +} + +/** + * Resolve a hostname (string) into an IP address. + * NON-BLOCKING callback version for use with raw API!!! + * + * Returns immediately with one of err_t return codes: + * - ERR_OK if hostname is a valid IP address string or the host + * name is already in the local names table. + * - ERR_INPROGRESS enqueue a request to be sent to the DNS server + * for resolution if no errors are present. + * - ERR_ARG: dns client not initialized or invalid hostname + * + * @param hostname the hostname that is to be queried + * @param addr pointer to a ip_addr_t where to store the address if it is already + * cached in the dns_table (only valid if ERR_OK is returned!) + * @param found a callback function to be called on success, failure or timeout (only if + * ERR_INPROGRESS is returned!) + * @param callback_arg argument to pass to the callback function + * @return a err_t return code. + */ +err_t ICACHE_FLASH_ATTR +dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, + void *callback_arg) +{ + u32_t ipaddr; + /* not initialized or no valid server yet, or invalid addr pointer + * or invalid hostname or invalid hostname length */ + if ((dns_pcb == NULL) || (addr == NULL) || + (!hostname) || (!hostname[0]) || + (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) { + return ERR_ARG; + } + +#if LWIP_HAVE_LOOPIF + if (strcmp(hostname, "localhost")==0) { + ip_addr_set_loopback(addr); + return ERR_OK; + } +#endif /* LWIP_HAVE_LOOPIF */ + + /* host name already in octet notation? set ip addr and return ERR_OK */ + ipaddr = ipaddr_addr(hostname); + if (ipaddr == IPADDR_NONE) { + /* already have this address cached? */ +// ipaddr = dns_lookup(hostname); + } + if (ipaddr != IPADDR_NONE) { + ip4_addr_set_u32(addr, ipaddr); + return ERR_OK; + } + + /* queue query with specified callback */ + return dns_enqueue(hostname, found, callback_arg); +} + +#endif /* LWIP_DNS */ diff --git a/app/lwip/core/init.c b/app/lwip/core/init.c new file mode 100644 index 00000000..3bd91755 --- /dev/null +++ b/app/lwip/core/init.c @@ -0,0 +1,321 @@ +/** + * @file + * Modules initialization + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/init.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/sockets.h" +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp_msg.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/timers.h" +#include "netif/etharp.h" + +/* Compile-time sanity checks for configuration errors. + * These can be done independently of LWIP_DEBUG, without penalty. + */ +#ifndef BYTE_ORDER + #error "BYTE_ORDER is not defined, you have to define it in your cc.h" +#endif +#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV) + #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h" +#endif +#if (!LWIP_ARP && ARP_QUEUEING) + #error "If you want to use ARP Queueing, you have to define LWIP_ARP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_UDPLITE) + #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DHCP) + #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_IGMP) + #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DNS) + #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0)) + #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h" +#endif +#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0)) + #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0)) + #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h" +#endif +//#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0)) +// #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h" +//#endif +#if (LWIP_TCP && (TCP_WND > 0xffff)) + #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff)) + #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2)) + #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work" +#endif +#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12))) + #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h" +#endif +#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)) + #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t" +#endif +#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1)) + #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h" +#endif +#if (LWIP_NETIF_API && (NO_SYS==1)) + #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1)) + #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0)) + #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h" +#endif +#if (!LWIP_NETCONN && LWIP_SOCKET) + #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP) + #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK) + #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h" +#endif +#if (!LWIP_ARP && LWIP_AUTOIP) + #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0)) + #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0)) + #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API))) + #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h" +#endif +/* There must be sufficient timeouts, taking into account requirements of the subsystems. */ +#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT)) + #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts" +#endif +#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS)) + #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!" +#endif +#if (MEM_LIBC_MALLOC && MEM_USE_POOLS) + #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h" +#endif +#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS) + #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h" +#endif +#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT) + #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf" +#endif +#if (TCP_QUEUE_OOSEQ && !LWIP_TCP) + #error "TCP_QUEUE_OOSEQ requires LWIP_TCP" +#endif +#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT))) + #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST" +#endif +#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT + #error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on" +#endif +#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT) + #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT" +#endif +#if LWIP_IGMP && !defined(LWIP_RAND) + #error "When using IGMP, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value" +#endif +#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING + #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too" +#endif +#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE + #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets" +#endif +#if IP_FRAG && IP_FRAG_USES_STATIC_BUF && LWIP_NETIF_TX_SINGLE_PBUF + #error "LWIP_NETIF_TX_SINGLE_PBUF does not work with IP_FRAG_USES_STATIC_BUF==1 as that creates pbuf queues" +#endif + + +/* Compile-time checks for deprecated options. + */ +#ifdef MEMP_NUM_TCPIP_MSG + #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef MEMP_NUM_API_MSG + #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef TCP_REXMIT_DEBUG + #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef RAW_STATS + #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_QUEUE_FIRST + #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_ALWAYS_INSERT + #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h." +#endif + +#ifdef LWIP_DEBUG +static void ICACHE_FLASH_ATTR +lwip_sanity_check(void) +{ + /* Warnings */ +#if LWIP_NETCONN + if (MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN\n")); +#endif /* LWIP_NETCONN */ +#if LWIP_TCP + if (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN\n")); + if (TCP_SND_BUF < 2 * TCP_MSS) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly\n")); + if (TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF/TCP_MSS))) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work\n")); + if (TCP_SNDLOWAT >= TCP_SND_BUF) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF.\n")); + if (TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN.\n")); + if (TCP_WND > (PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE)) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE\n")); + if (TCP_WND < TCP_MSS) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is smaller than MSS\n")); +#endif /* LWIP_TCP */ +#if LWIP_SOCKET + /* Check that the SO_* socket options and SOF_* lwIP-internal flags match */ + if (SO_ACCEPTCONN != SOF_ACCEPTCONN) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_ACCEPTCONN != SOF_ACCEPTCONN\n")); + if (SO_REUSEADDR != SOF_REUSEADDR) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_REUSEADDR != SOF_REUSEADDR\n")); + if (SO_KEEPALIVE != SOF_KEEPALIVE) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_KEEPALIVE != SOF_KEEPALIVE\n")); + if (SO_BROADCAST != SOF_BROADCAST) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_BROADCAST != SOF_BROADCAST\n")); + if (SO_LINGER != SOF_LINGER) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_LINGER != SOF_LINGER\n")); +#endif /* LWIP_SOCKET */ +} +#else /* LWIP_DEBUG */ +#define lwip_sanity_check() +#endif /* LWIP_DEBUG */ + +/** + * Perform Sanity check of user-configurable values, and initialize all modules. + */ +void +lwip_init(void) +{ + /* Sanity check user-configurable values */ + lwip_sanity_check(); + + /* Modules initialization */ + stats_init(); +#if !NO_SYS + sys_init(); +#endif /* !NO_SYS */ +#if 0 + mem_init(&_bss_end); +#endif + memp_init(); + + pbuf_init(); + + netif_init(); + +#if LWIP_SOCKET + lwip_socket_init(); +#endif /* LWIP_SOCKET */ + ip_init(); + +#if LWIP_ARP + etharp_init(); + +#endif /* LWIP_ARP */ +#if LWIP_RAW + raw_init(); + +#endif /* LWIP_RAW */ +#if LWIP_UDP + udp_init(); + +#endif /* LWIP_UDP */ +#if LWIP_TCP + MEMP_NUM_TCP_PCB = 5; + tcp_init(); + +#endif /* LWIP_TCP */ +#if LWIP_SNMP + snmp_init(); + +#endif /* LWIP_SNMP */ +#if LWIP_AUTOIP + autoip_init(); + +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + igmp_init(); + +#endif /* LWIP_IGMP */ +#if LWIP_DNS + dns_init(); + +#endif /* LWIP_DNS */ + +#if LWIP_TIMERS + sys_timeouts_init(); +#endif /* LWIP_TIMERS */ +} diff --git a/app/lwip/core/ipv4/Makefile b/app/lwip/core/ipv4/Makefile new file mode 100644 index 00000000..660968be --- /dev/null +++ b/app/lwip/core/ipv4/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = liblwipipv4.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/lwip/core/ipv4/autoip.c b/app/lwip/core/ipv4/autoip.c new file mode 100644 index 00000000..92bb4591 --- /dev/null +++ b/app/lwip/core/ipv4/autoip.c @@ -0,0 +1,536 @@ +/** + * @file + * AutoIP Automatic LinkLocal IP Configuration + * + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +/******************************************************************************* + * USAGE: + * + * define LWIP_AUTOIP 1 in your lwipopts.h + * + * If you don't use tcpip.c (so, don't call, you don't call tcpip_init): + * - First, call autoip_init(). + * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces, + * that should be defined in autoip.h. + * I recommend a value of 100. The value must divide 1000 with a remainder almost 0. + * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 .... + * + * Without DHCP: + * - Call autoip_start() after netif_add(). + * + * With DHCP: + * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h. + * - Configure your DHCP Client. + * + */ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" + +#include +#include + +/* 169.254.0.0 */ +#define AUTOIP_NET 0xA9FE0000 +/* 169.254.1.0 */ +#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100) +/* 169.254.254.255 */ +#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF) + + +/** Pseudo random macro based on netif informations. + * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */ +#ifndef LWIP_AUTOIP_RAND +#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \ + ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \ + ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \ + ((u32_t)((netif->hwaddr[4]) & 0xff))) + \ + (netif->autoip?netif->autoip->tried_llipaddr:0)) +#endif /* LWIP_AUTOIP_RAND */ + +/** + * Macro that generates the initial IP address to be tried by AUTOIP. + * If you want to override this, define it to something else in lwipopts.h. + */ +#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR +#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \ + htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \ + ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8))) +#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */ + +/* static functions */ +static void autoip_handle_arp_conflict(struct netif *netif); + +/* creates a pseudo random LL IP-Address for a network interface */ +static void autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr); + +/* sends an ARP probe */ +static err_t autoip_arp_probe(struct netif *netif); + +/* sends an ARP announce */ +static err_t autoip_arp_announce(struct netif *netif); + +/* configure interface for use with current LL IP-Address */ +static err_t autoip_bind(struct netif *netif); + +/* start sending probes for llipaddr */ +static void autoip_start_probing(struct netif *netif); + +/** + * Initialize this module + */ +void +autoip_init(void) +{ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_init()\n")); +} + +/** Set a statically allocated struct autoip to work with. + * Using this prevents autoip_start to allocate it using mem_malloc. + * + * @param netif the netif for which to set the struct autoip + * @param dhcp (uninitialised) dhcp struct allocated by the application + */ +void +autoip_set_struct(struct netif *netif, struct autoip *autoip) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("autoip != NULL", autoip != NULL); + LWIP_ASSERT("netif already has a struct autoip set", netif->autoip == NULL); + + /* clear data structure */ + memset(autoip, 0, sizeof(struct autoip)); + /* autoip->state = AUTOIP_STATE_OFF; */ + netif->autoip = autoip; +} + +/** Restart AutoIP client and check the next address (conflict detected) + * + * @param netif The netif under AutoIP control + */ +static void +autoip_restart(struct netif *netif) +{ + netif->autoip->tried_llipaddr++; + autoip_start(netif); +} + +/** + * Handle a IP address conflict after an ARP conflict detection + */ +static void +autoip_handle_arp_conflict(struct netif *netif) +{ + /* Somehow detect if we are defending or retreating */ + unsigned char defend = 1; /* tbd */ + + if(defend) { + if(netif->autoip->lastconflict > 0) { + /* retreat, there was a conflicting ARP in the last + * DEFEND_INTERVAL seconds + */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n")); + + /* TODO: close all TCP sessions */ + autoip_restart(netif); + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n")); + autoip_arp_announce(netif); + netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we do not defend, retreating\n")); + /* TODO: close all TCP sessions */ + autoip_restart(netif); + } +} + +/** + * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * + * @param netif network interface on which create the IP-Address + * @param ipaddr ip address to initialize + */ +static void +autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr) +{ + /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * compliant to RFC 3927 Section 2.1 + * We have 254 * 256 possibilities */ + + u32_t addr = ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif)); + addr += netif->autoip->tried_llipaddr; + addr = AUTOIP_NET | (addr & 0xffff); + /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */ + + if (addr < AUTOIP_RANGE_START) { + addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + if (addr > AUTOIP_RANGE_END) { + addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) && + (addr <= AUTOIP_RANGE_END)); + ip4_addr_set_u32(ipaddr, htonl(addr)); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + (u16_t)(netif->autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), + ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); +} + +/** + * Sends an ARP probe from a network interface + * + * @param netif network interface used to send the probe + */ +static err_t +autoip_arp_probe(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Sends an ARP announce from a network interface + * + * @param netif network interface used to send the announce + */ +static err_t +autoip_arp_announce(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Configure interface for use with current LL IP-Address + * + * @param netif network interface to configure with current LL IP-Address + */ +static err_t +autoip_bind(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + ip_addr_t sn_mask, gw_addr; + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num, + ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr), + ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr))); + + IP4_ADDR(&sn_mask, 255, 255, 0, 0); + IP4_ADDR(&gw_addr, 0, 0, 0, 0); + + netif_set_ipaddr(netif, &autoip->llipaddr); + netif_set_netmask(netif, &sn_mask); + netif_set_gw(netif, &gw_addr); + + /* bring the interface up */ + netif_set_up(netif); + + return ERR_OK; +} + +/** + * Start AutoIP client + * + * @param netif network interface on which start the AutoIP client + */ +err_t +autoip_start(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + err_t result = ERR_OK; + + if(netif_is_up(netif)) { + netif_set_down(netif); + } + + /* Set IP-Address, Netmask and Gateway to 0 to make sure that + * ARP Packets are formed correctly + */ + ip_addr_set_zero(&netif->ip_addr); + ip_addr_set_zero(&netif->netmask); + ip_addr_set_zero(&netif->gw); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], + netif->name[1], (u16_t)netif->num)); + if(autoip == NULL) { + /* no AutoIP client attached yet? */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): starting new AUTOIP client\n")); + autoip = (struct autoip *)mem_malloc(sizeof(struct autoip)); + if(autoip == NULL) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): could not allocate autoip\n")); + return ERR_MEM; + } + memset(autoip, 0, sizeof(struct autoip)); + /* store this AutoIP client in the netif */ + netif->autoip = autoip; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip")); + } else { + autoip->state = AUTOIP_STATE_OFF; + autoip->ttw = 0; + autoip->sent_num = 0; + ip_addr_set_zero(&autoip->llipaddr); + autoip->lastconflict = 0; + } + + autoip_create_addr(netif, &(autoip->llipaddr)); + autoip_start_probing(netif); + + return result; +} + +static void +autoip_start_probing(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + + autoip->state = AUTOIP_STATE_PROBING; + autoip->sent_num = 0; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + + /* time to wait to first probe, this is randomly + * choosen out of 0 to PROBE_WAIT seconds. + * compliant to RFC 3927 Section 2.2.1 + */ + autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND)); + + /* + * if we tried more then MAX_CONFLICTS we must limit our rate for + * accquiring and probing address + * compliant to RFC 3927 Section 2.2.1 + */ + if(autoip->tried_llipaddr > MAX_CONFLICTS) { + autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } +} + +/** + * Handle a possible change in the network configuration. + * + * If there is an AutoIP address configured, take the interface down + * and begin probing with the same address. + */ +void +autoip_network_changed(struct netif *netif) +{ + if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) { + netif_set_down(netif); + autoip_start_probing(netif); + } +} + +/** + * Stop AutoIP client + * + * @param netif network interface on which stop the AutoIP client + */ +err_t +autoip_stop(struct netif *netif) +{ + netif->autoip->state = AUTOIP_STATE_OFF; + netif_set_down(netif); + return ERR_OK; +} + +/** + * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds + */ +void +autoip_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on AutoIP configured interfaces */ + if (netif->autoip != NULL) { + if(netif->autoip->lastconflict > 0) { + netif->autoip->lastconflict--; + } + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n", + (u16_t)(netif->autoip->state), netif->autoip->ttw)); + + switch(netif->autoip->state) { + case AUTOIP_STATE_PROBING: + if(netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if(netif->autoip->sent_num >= PROBE_NUM) { + netif->autoip->state = AUTOIP_STATE_ANNOUNCING; + netif->autoip->sent_num = 0; + netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + } else { + autoip_arp_probe(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() PROBING Sent Probe\n")); + netif->autoip->sent_num++; + /* calculate time to wait to next probe */ + netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) % + ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) + + PROBE_MIN * AUTOIP_TICKS_PER_SECOND); + } + } + break; + + case AUTOIP_STATE_ANNOUNCING: + if(netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if(netif->autoip->sent_num == 0) { + /* We are here the first time, so we waited ANNOUNCE_WAIT seconds + * Now we can bind to an IP address and use it. + * + * autoip_bind calls netif_set_up. This triggers a gratuitous ARP + * which counts as an announcement. + */ + autoip_bind(netif); + } else { + autoip_arp_announce(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() ANNOUNCING Sent Announce\n")); + } + netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND; + netif->autoip->sent_num++; + + if(netif->autoip->sent_num >= ANNOUNCE_NUM) { + netif->autoip->state = AUTOIP_STATE_BOUND; + netif->autoip->sent_num = 0; + netif->autoip->ttw = 0; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + } + } + break; + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * Handles every incoming ARP Packet, called by etharp_arp_input. + * + * @param netif network interface to use for autoip processing + * @param hdr Incoming ARP packet + */ +void +autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr) +{ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n")); + if ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) { + /* when ip.src == llipaddr && hw.src != netif->hwaddr + * + * when probing ip.dst == llipaddr && hw.src != netif->hwaddr + * we have a conflict and must solve it + */ + ip_addr_t sipaddr, dipaddr; + struct eth_addr netifaddr; + ETHADDR16_COPY(netifaddr.addr, netif->hwaddr); + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). + */ + IPADDR2_COPY(&sipaddr, &hdr->sipaddr); + IPADDR2_COPY(&dipaddr, &hdr->dipaddr); + + if ((netif->autoip->state == AUTOIP_STATE_PROBING) || + ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) && + (netif->autoip->sent_num == 0))) { + /* RFC 3927 Section 2.2.1: + * from beginning to after ANNOUNCE_WAIT + * seconds we have a conflict if + * ip.src == llipaddr OR + * ip.dst == llipaddr && hw.src != own hwaddr + */ + if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) || + (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Probe Conflict detected\n")); + autoip_restart(netif); + } + } else { + /* RFC 3927 Section 2.5: + * in any state we have a conflict if + * ip.src == llipaddr && hw.src != own hwaddr + */ + if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Conflicting ARP-Packet detected\n")); + autoip_handle_arp_conflict(netif); + } + } + } +} + +#endif /* LWIP_AUTOIP */ diff --git a/app/lwip/core/ipv4/icmp.c b/app/lwip/core/ipv4/icmp.c new file mode 100644 index 00000000..2eb3b02c --- /dev/null +++ b/app/lwip/core/ipv4/icmp.c @@ -0,0 +1,338 @@ +/** + * @file + * ICMP - Internet Control Message Protocol + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* Some ICMP messages should be passed to the transport protocols. This + is not implemented. */ + +#include "lwip/opt.h" + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwipopts.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" + +#include + +/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be + * used to modify and send a response packet (and to 1 if this is not the case, + * e.g. when link header is stripped of when receiving) */ +#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1 +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + +/* The amount of data from the original packet to return in a dest-unreachable */ +#define ICMP_DEST_UNREACH_DATASIZE 8 + +static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code); + +/** + * Processes ICMP input packets, called from ip_input(). + * + * Currently only processes icmp echo requests and sends + * out the echo response. + * + * @param p the icmp echo request packet, p->payload pointing to the ip header + * @param inp the netif on which this packet was received + */ +void +icmp_input(struct pbuf *p, struct netif *inp) +{ + u8_t type; +#ifdef LWIP_DEBUG + u8_t code; +#endif /* LWIP_DEBUG */ + struct icmp_echo_hdr *iecho; + struct ip_hdr *iphdr; + s16_t hlen; + + ICMP_STATS_INC(icmp.recv); + snmp_inc_icmpinmsgs(); + + + iphdr = (struct ip_hdr *)p->payload; + hlen = IPH_HL(iphdr) * 4; + if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len)); + goto lenerr; + } + + type = *((u8_t *)p->payload); +#ifdef LWIP_DEBUG + code = *(((u8_t *)p->payload)+1); +#endif /* LWIP_DEBUG */ + switch (type) { + case ICMP_ER: + /* This is OK, echo reply might have been parsed by a raw PCB + (as obviously, an echo request has been sent, too). */ + break; + case ICMP_ECHO: +#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING + { + int accepted = 1; +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip_addr_ismulticast(¤t_iphdr_dest)) { + accepted = 0; + } +#endif /* LWIP_MULTICAST_PING */ +#if !LWIP_BROADCAST_PING + /* broadcast destination address? */ + if (ip_addr_isbroadcast(¤t_iphdr_dest, inp)) { + accepted = 0; + } +#endif /* LWIP_BROADCAST_PING */ + /* broadcast or multicast destination address not acceptd? */ + if (!accepted) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n")); + ICMP_STATS_INC(icmp.err); + pbuf_free(p); + return; + } + } +#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */ + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n")); + if (p->tot_len < sizeof(struct icmp_echo_hdr)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n")); + goto lenerr; + } + if (inet_chksum_pbuf(p) != 0) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n")); + pbuf_free(p); + ICMP_STATS_INC(icmp.chkerr); + snmp_inc_icmpinerrors(); + return; + } +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN + if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + /* p is not big enough to contain link headers + * allocate a new one and copy p into it + */ + struct pbuf *r; + /* switch p->payload to ip header */ + if (pbuf_header(p, hlen)) { + LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0); + goto memerr; + } + /* allocate new packet buffer with space for link headers */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n")); + goto memerr; + } + LWIP_ASSERT("check that first pbuf can hold struct the ICMP header", + (r->len >= hlen + sizeof(struct icmp_echo_hdr))); + /* copy the whole packet including ip header */ + if (pbuf_copy(r, p) != ERR_OK) { + LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0); + goto memerr; + } + iphdr = (struct ip_hdr *)r->payload; + /* switch r->payload back to icmp header */ + if (pbuf_header(r, -hlen)) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + /* free the original p */ + pbuf_free(p); + /* we now have an identical copy of p that has room for link headers */ + p = r; + } else { + /* restore p->payload to point to icmp header */ + if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + } +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + /* At this point, all checks are OK. */ + /* We generate an answer by switching the dest and src ip addresses, + * setting the icmp type to ECHO_RESPONSE and updating the checksum. */ + iecho = (struct icmp_echo_hdr *)p->payload; + ip_addr_copy(iphdr->src, *ip_current_dest_addr()); + ip_addr_copy(iphdr->dest, *ip_current_src_addr()); + ICMPH_TYPE_SET(iecho, ICMP_ER); + /* adjust the checksum */ + if (iecho->chksum >= PP_HTONS(0xffff - (ICMP_ECHO << 8))) { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1; + } else { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8); + } + + /* Set the correct TTL and recalculate the header checksum. */ + IPH_TTL_SET(iphdr, ICMP_TTL); + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); +#endif /* CHECKSUM_GEN_IP */ + + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of echo replies attempted to send */ + snmp_inc_icmpoutechoreps(); + + if(pbuf_header(p, hlen)) { + LWIP_ASSERT("Can't move over header in packet", 0); + } else { + err_t ret; + /* send an ICMP packet, src addr is the dest addr of the curren packet */ + ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL, + ICMP_TTL, 0, IP_PROTO_ICMP, inp); + if (ret != ERR_OK) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret)); + } + } + break; + default: + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n", + (s16_t)type, (s16_t)code)); + ICMP_STATS_INC(icmp.proterr); + ICMP_STATS_INC(icmp.drop); + } + pbuf_free(p); + return; +lenerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + snmp_inc_icmpinerrors(); + return; +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +memerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.err); + snmp_inc_icmpinerrors(); + return; +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ +} + +/** + * Send an icmp 'destination unreachable' packet, called from ip_input() if + * the transport layer protocol is unknown and from udp_input() if the local + * port is not bound. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'unreachable' packet + */ +void +icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +{ + icmp_send_response(p, ICMP_DUR, t); +} + +#if IP_FORWARD || IP_REASSEMBLY +/** + * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0. + * + * @param p the input packet for which the 'time exceeded' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'time exceeded' packet + */ +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + icmp_send_response(p, ICMP_TE, t); +} + +#endif /* IP_FORWARD || IP_REASSEMBLY */ + +/** + * Send an icmp packet in response to an incoming packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param type Type of the ICMP header + * @param code Code of the ICMP header + */ +static void ICACHE_FLASH_ATTR +icmp_send_response(struct pbuf *p, u8_t type, u8_t code) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + /* we can use the echo header here */ + struct icmp_echo_hdr *icmphdr; + ip_addr_t iphdr_src; + + /* ICMP header + IP header + 8 bytes of data */ + //Ϊpbufռ䣬pbufԤIPײ̫ײռ䣬pbuf + //=ײ+ݳ(IPײ+8) + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE, + PBUF_RAM); + if (q == NULL) {//ʧܣ + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE))); + + iphdr = (struct ip_hdr *)p->payload;//ָIPݰײ + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src)); + LWIP_DEBUGF(ICMP_DEBUG, (" to ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest)); + LWIP_DEBUGF(ICMP_DEBUG, ("\n")); + + icmphdr = (struct icmp_echo_hdr *)q->payload;//ָײ + icmphdr->type = type;//дֶ + icmphdr->code = code;//дֶ + icmphdr->id = 0;//ĿIJɴݱʱ + icmphdr->seqno = 0;//ģײʣ4ֽڶΪ0 + + /* copy fields from original packet IPݱIPײ+8ֽݿ*/ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload, + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE); + + /* calculate checksum */ + icmphdr->chksum = 0;//Уֶ0 + icmphdr->chksum = inet_chksum(icmphdr, q->len);//дУ + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of destination unreachable messages attempted to send */ + snmp_inc_icmpouttimeexcds(); + ip_addr_copy(iphdr_src, iphdr->src); + ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP);//IP㺯ICMP + pbuf_free(q); +} + +#endif /* LWIP_ICMP */ diff --git a/app/lwip/core/ipv4/igmp.c b/app/lwip/core/ipv4/igmp.c new file mode 100644 index 00000000..b4cca192 --- /dev/null +++ b/app/lwip/core/ipv4/igmp.c @@ -0,0 +1,817 @@ +/** + * @file + * IGMP - Internet Group Management Protocol + * + */ + +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +/*------------------------------------------------------------- +Note 1) +Although the rfc requires V1 AND V2 capability +we will only support v2 since now V1 is very old (August 1989) +V1 can be added if required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 2) +A query for a specific group address (as opposed to ALLHOSTS) +has now been implemented as I am unsure if it is required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 3) +The router alert rfc 2113 is implemented in outgoing packets +but not checked rigorously incoming +------------------------------------------------------------- +Steve Reynolds +------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * RFC 988 - Host extensions for IP multicasting - V0 + * RFC 1054 - Host extensions for IP multicasting - + * RFC 1112 - Host extensions for IP multicasting - V1 + * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) + * RFC 3376 - Internet Group Management Protocol, Version 3 - V3 + * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ + * RFC 2113 - IP Router Alert Option - + *----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/igmp.h" +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/stats.h" + +#include "string.h" + +/* + * IGMP constants + */ +#define IGMP_TTL 1 +#define IGMP_MINLEN 8 +#define ROUTER_ALERT 0x9404 +#define ROUTER_ALERTLEN 4 + +/* + * IGMP message types, including version number. + */ +#define IGMP_MEMB_QUERY 0x11 /* Membership query */ +#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */ +#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */ +#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */ + +/* Group membership states */ +#define IGMP_GROUP_NON_MEMBER 0 +#define IGMP_GROUP_DELAYING_MEMBER 1 +#define IGMP_GROUP_IDLE_MEMBER 2 + +/** + * IGMP packet format. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct igmp_msg { + PACK_STRUCT_FIELD(u8_t igmp_msgtype); + PACK_STRUCT_FIELD(u8_t igmp_maxresp); + PACK_STRUCT_FIELD(u16_t igmp_checksum); + PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)ICACHE_FLASH_ATTR; +static err_t igmp_remove_group(struct igmp_group *group)ICACHE_FLASH_ATTR; +static void igmp_timeout( struct igmp_group *group)ICACHE_FLASH_ATTR; +static void igmp_start_timer(struct igmp_group *group, u8_t max_time)ICACHE_FLASH_ATTR; +static void igmp_stop_timer(struct igmp_group *group)ICACHE_FLASH_ATTR; +static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp)ICACHE_FLASH_ATTR; +static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)ICACHE_FLASH_ATTR; +static void igmp_send(struct igmp_group *group, u8_t type)ICACHE_FLASH_ATTR; + + +static struct igmp_group* igmp_group_list; +static ip_addr_t allsystems; +static ip_addr_t allrouters; + + +/** + * Initialize the IGMP module + */ +void +igmp_init(void) +{ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); + + IP4_ADDR(&allsystems, 224, 0, 0, 1); + IP4_ADDR(&allrouters, 224, 0, 0, 2); +} + +#ifdef LWIP_DEBUG +/** + * Dump global IGMP groups list + */ +void +igmp_dump_group_list() +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state))); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); + group = group->next; + } + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); +} +#else +#define igmp_dump_group_list() +#endif /* LWIP_DEBUG */ + +/** + * Start IGMP processing on interface + * + * @param netif network interface on which start IGMP processing + */ +err_t +igmp_start(struct netif *netif) +{ + struct igmp_group* group; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif)); + + group = igmp_lookup_group(netif, &allsystems); + + if (group != NULL) { + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->use++; + + /* Allow the igmp messages at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, &allsystems); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER); + } + + return ERR_OK; + } + + return ERR_MEM; +} + +/** + * Stop IGMP processing on interface + * + * @param netif network interface on which stop IGMP processing + */ +err_t +igmp_stop(struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + struct igmp_group *prev = NULL; + struct igmp_group *next; + + /* look for groups joined on this interface further down the list */ + while (group != NULL) { + next = group->next; + /* is it a group joined on this interface? */ + if (group->netif == netif) { + /* is it the first group of the list? */ + if (group == igmp_group_list) { + igmp_group_list = next; + } + /* is there a "previous" group defined? */ + if (prev != NULL) { + prev->next = next; + } + /* disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER); + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + } else { + /* change the "previous" */ + prev = group; + } + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report IGMP memberships for this interface + * + * @param netif network interface on which report IGMP memberships + */ +void +igmp_report_groups(struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif)); + + while (group != NULL) { + if (group->netif == netif) { + igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + } + group = group->next; + } +} + +/** + * Search for a group in the global igmp_group_list + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search for + * @return a struct igmp_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct igmp_group * +igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) { + return group; + } + group = group->next; + } + + /* to be clearer, we return NULL here instead of + * 'group' (which is also NULL at this point). + */ + return NULL; +} + +/** + * Search for a specific igmp group and create a new one if not found- + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search + * @return a struct igmp_group*, + * NULL on memory error. + */ +struct igmp_group * +igmp_lookup_group(struct netif *ifp, ip_addr_t *addr) +{ + struct igmp_group *group = igmp_group_list; + + /* Search if the group already exists */ + group = igmp_lookfor_group(ifp, addr); + if (group != NULL) { + /* Group already exists. */ + return group; + } + + /* Group doesn't exist yet, create a new one */ + group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP); + if (group != NULL) { + group->netif = ifp; + ip_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = IGMP_GROUP_NON_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = igmp_group_list; + + igmp_group_list = group; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to "))); + ip_addr_debug_print(IGMP_DEBUG, addr); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp)); + + return group; +} + +/** + * Remove a group in the global igmp_group_list + * + * @param group the group to remove from the global igmp_group_list + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +static err_t +igmp_remove_group(struct igmp_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (igmp_group_list == group) { + igmp_group_list = group->next; + } else { + /* look for group further down the list */ + struct igmp_group *tmpGroup; + for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not found in the global igmp_group_list */ + if (tmpGroup == NULL) + err = ERR_ARG; + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + + return err; +} + +/** + * Called from ip_input() if a new IGMP packet is received. + * + * @param p received igmp packet, p->payload pointing to the ip header + * @param inp network interface on which the packet was received + * @param dest destination ip address of the igmp packet + */ +void +igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest) +{ + struct ip_hdr * iphdr; + struct igmp_msg* igmp; + struct igmp_group* group; + struct igmp_group* groupref; + + IGMP_STATS_INC(igmp.recv); + + /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ + iphdr = (struct ip_hdr *)p->payload; + if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) { + pbuf_free(p); + IGMP_STATS_INC(igmp.lenerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); + return; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); + ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src)); + LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); + ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp)); + + /* Now calculate and check the checksum */ + igmp = (struct igmp_msg *)p->payload; + if (inet_chksum(igmp, p->len)) { + pbuf_free(p); + IGMP_STATS_INC(igmp.chkerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); + return; + } + + /* Packet is ok so find an existing group */ + group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */ + + /* If group can be found or create... */ + if (!group) { + pbuf_free(p); + IGMP_STATS_INC(igmp.drop); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); + return; + } + + /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ + switch (igmp->igmp_msgtype) { + case IGMP_MEMB_QUERY: { + /* IGMP_MEMB_QUERY to the "all systems" address ? */ + if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) { + /* THIS IS THE GENERAL QUERY */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + + if (igmp->igmp_maxresp == 0) { + IGMP_STATS_INC(igmp.rx_v1); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); + igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; + } else { + IGMP_STATS_INC(igmp.rx_general); + } + + groupref = igmp_group_list; + while (groupref) { + /* Do not send messages on the all systems group address! */ + if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) { + igmp_delaying_member(groupref, igmp->igmp_maxresp); + } + groupref = groupref->next; + } + } else { + /* IGMP_MEMB_QUERY to a specific group ? */ + if (!ip_addr_isany(&igmp->igmp_group_address)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); + ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address); + if (ip_addr_cmp(dest, &allsystems)) { + ip_addr_t groupaddr; + LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + /* we first need to re-look for the group since we used dest last time */ + ip_addr_copy(groupaddr, igmp->igmp_group_address); + group = igmp_lookfor_group(inp, &groupaddr); + } else { + LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + } + + if (group != NULL) { + IGMP_STATS_INC(igmp.rx_group); + igmp_delaying_member(group, igmp->igmp_maxresp); + } else { + IGMP_STATS_INC(igmp.drop); + } + } else { + IGMP_STATS_INC(igmp.proterr); + } + } + break; + } + case IGMP_V2_MEMB_REPORT: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); + IGMP_STATS_INC(igmp.rx_report); + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + /* This is on a specific group we have already looked up */ + group->timer = 0; /* stopped */ + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + break; + } + default: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", + igmp->igmp_msgtype, group->group_state, &group, group->netif)); + IGMP_STATS_INC(igmp.proterr); + break; + } + } + + pbuf_free(p); + return; +} + +/** + * Join a group on one network interface. + * + * @param ifaddr ip address of the network interface which should join a new group + * @param groupaddr the ip address of the group which to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group or create a new one if not found */ + group = igmp_lookup_group(netif, groupaddr); + + if (group != NULL) { + /* This should create a new group, check the state to make sure */ + if (group->group_state != IGMP_GROUP_NON_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n")); + } else { + /* OK - it was new group */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If first use of the group, allow the group at the MAC level */ + if ((group->use==0) && (netif->igmp_mac_filter != NULL)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER); + } + + IGMP_STATS_INC(igmp.tx_join); + igmp_send(group, IGMP_V2_MEMB_REPORT); + + igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + + /* Need to work out where this timer comes from */ + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } + /* Increment group use */ + group->use++; + /* Join on this interface */ + err = ERR_OK; + } else { + /* Return an error even if some network interfaces are joined */ + /** @todo undo any other netif already joined */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n")); + return ERR_MEM; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * Leave a group on one network interface. + * + * @param ifaddr ip address of the network interface which should leave a group + * @param groupaddr the ip address of the group which to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group */ + group = igmp_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Only send a leave if the flag is set according to the state diagram */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If there is no other use of the group */ + if (group->use <= 1) { + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n")); + IGMP_STATS_INC(igmp.tx_leave); + igmp_send(group, IGMP_LEAVE_GROUP); + } + + /* Disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER); + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* Free the group */ + igmp_remove_group(group); + } else { + /* Decrement group use */ + group->use--; + } + /* Leave on this interface */ + err = ERR_OK; + } else { + /* It's not a fatal error on "leavegroup" */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n")); + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * The igmp timer function (both for NO_SYS=1 and =0) + * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). + */ +void +igmp_tmr(void) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { + igmp_timeout(group); + } + } + group = group->next; + } +} + +/** + * Called if a timeout for one group is reached. + * Sends a report for this group. + * + * @param group an igmp_group for which a timeout is reached + */ +static void +igmp_timeout(struct igmp_group *group) +{ + /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); + ip_addr_debug_print(IGMP_DEBUG, &(group->group_address)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); + + IGMP_STATS_INC(igmp.tx_report); + igmp_send(group, IGMP_V2_MEMB_REPORT); + } +} + +/** + * Start a timer for an igmp group + * + * @param group the igmp_group for which to start a timer + * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with + * every call to igmp_tmr()) + */ +static void +igmp_start_timer(struct igmp_group *group, u8_t max_time) +{ + /* ensure the input value is > 0 */ + if (max_time == 0) { + max_time = 1; + } + /* ensure the random value is > 0 */ + group->timer = (LWIP_RAND() % (max_time - 1)) + 1; +} + +/** + * Stop a timer for an igmp_group + * + * @param group the igmp_group for which to stop the timer + */ +static void +igmp_stop_timer(struct igmp_group *group) +{ + group->timer = 0; +} + +/** + * Delaying membership report for a group if necessary + * + * @param group the igmp_group for which "delaying" membership report + * @param maxresp query delay + */ +static void +igmp_delaying_member(struct igmp_group *group, u8_t maxresp) +{ + if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || + ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && + ((group->timer == 0) || (maxresp < group->timer)))) { + igmp_start_timer(group, maxresp); + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } +} + + +/** + * Sends an IP packet on a network interface. This function constructs the IP header + * and calculates the IP header checksum. If the source IP address is NULL, + * the IP address of the outgoing network interface is filled in as source address. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + */ +static err_t +igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif) +{ + /* This is the "router alert" option */ + u16_t ra[2]; + ra[0] = PP_HTONS(ROUTER_ALERT); + ra[1] = 0x0000; /* Router shall examine packet */ + IGMP_STATS_INC(igmp.xmit); + return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN); +} + +/** + * Send an igmp packet to a specific group. + * + * @param group the group to which to send the packet + * @param type the type of igmp packet to send + */ +static void +igmp_send(struct igmp_group *group, u8_t type) +{ + struct pbuf* p = NULL; + struct igmp_msg* igmp = NULL; + ip_addr_t src = *IP_ADDR_ANY; + ip_addr_t* dest = NULL; + + /* IP header + "router alert" option + IGMP header */ + p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); + + if (p) { + igmp = (struct igmp_msg *)p->payload; + LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", + (p->len >= sizeof(struct igmp_msg))); + ip_addr_copy(src, group->netif->ip_addr); + + if (type == IGMP_V2_MEMB_REPORT) { + dest = &(group->group_address); + ip_addr_copy(igmp->igmp_group_address, group->group_address); + group->last_reporter_flag = 1; /* Remember we were the last to report */ + } else { + if (type == IGMP_LEAVE_GROUP) { + dest = &allrouters; + ip_addr_copy(igmp->igmp_group_address, group->group_address); + } + } + + if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { + igmp->igmp_msgtype = type; + igmp->igmp_maxresp = 0; + igmp->igmp_checksum = 0; + igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN); + + igmp_ip_output_if(p, &src, dest, group->netif); + } + + pbuf_free(p); + } else { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); + IGMP_STATS_INC(igmp.memerr); + } +} + +#endif /* LWIP_IGMP */ diff --git a/app/lwip/core/ipv4/inet.c b/app/lwip/core/ipv4/inet.c new file mode 100644 index 00000000..e283a576 --- /dev/null +++ b/app/lwip/core/ipv4/inet.c @@ -0,0 +1,42 @@ +/** + * @file + * Functions common to all TCP/IPv4 modules, such as the byte order functions. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet.h" + diff --git a/app/lwip/core/ipv4/inet_chksum.c b/app/lwip/core/ipv4/inet_chksum.c new file mode 100644 index 00000000..bfcc40d5 --- /dev/null +++ b/app/lwip/core/ipv4/inet_chksum.c @@ -0,0 +1,450 @@ +/** + * @file + * Incluse internet checksum functions. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet_chksum.h" +#include "lwip/def.h" + +#include +#include + +/* These are some reference implementations of the checksum algorithm, with the + * aim of being simple, correct and fully portable. Checksumming is the + * first thing you would want to optimize for your platform. If you create + * your own version, link it in and in your cc.h put: + * + * #define LWIP_CHKSUM + * + * Or you can select from the implementations below by defining + * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3. + */ + +#ifndef LWIP_CHKSUM +# define LWIP_CHKSUM lwip_standard_chksum +# ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 2 +# endif +#endif +/* If none set: */ +#ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 0 +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */ +/** + * lwip checksum + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * @note accumulator size limits summable length to 64k + * @note host endianess is irrelevant (p3 RFC1071) + */ +static u16_t ICACHE_FLASH_ATTR +lwip_standard_chksum(void *dataptr, u16_t len) +{ + u32_t acc; + u16_t src; + u8_t *octetptr; + + acc = 0; + /* dataptr may be at odd or even addresses */ + octetptr = (u8_t*)dataptr; + while (len > 1) { + /* declare first octet as most significant + thus assume network order, ignoring host order */ + src = (*octetptr) << 8; + octetptr++; + /* declare second octet as least significant */ + src |= (*octetptr); + octetptr++; + acc += src; + len -= 2; + } + if (len > 0) { + /* accumulate remaining octet */ + src = (*octetptr) << 8; + acc += src; + } + /* add deferred carry bits */ + acc = (acc >> 16) + (acc & 0x0000ffffUL); + if ((acc & 0xffff0000UL) != 0) { + acc = (acc >> 16) + (acc & 0x0000ffffUL); + } + /* This maybe a little confusing: reorder sum using htons() + instead of ntohs() since it has a little less call overhead. + The caller must invert bits for Internet sum ! */ + return htons((u16_t)acc); +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */ +/* + * Curt McDowell + * Broadcom Corp. + * csm@broadcom.com + * + * IP checksum two bytes at a time with support for + * unaligned buffer. + * Works for len up to and including 0x20000. + * by Curt McDowell, Broadcom Corp. 12/08/2005 + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + */ + +static u16_t ICACHE_FLASH_ATTR +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = (u8_t *)dataptr; + u16_t *ps, t = 0; + u32_t sum = 0; + int odd = ((mem_ptr_t)pb & 1); + + /* Get aligned to u16_t */ + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + /* Add the bulk of the data */ + ps = (u16_t *)(void *)pb; + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* Consume left-over byte, if any */ + if (len > 0) { + ((u8_t *)&t)[0] = *(u8_t *)ps; + } + + /* Add end bytes */ + sum += t; + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + /* Swap if alignment was odd */ + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return (u16_t)sum; +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */ +/** + * An optimized checksum routine. Basically, it uses loop-unrolling on + * the checksum loop, treating the head and tail bytes specially, whereas + * the inner loop acts on 8 bytes at a time. + * + * @arg start of buffer to be checksummed. May be an odd byte address. + * @len number of bytes in the buffer to be checksummed. + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * by Curt McDowell, Broadcom Corp. December 8th, 2005 + */ + +static u16_t ICACHE_FLASH_ATTR +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = (u8_t *)dataptr; + u16_t *ps, t = 0; + u32_t *pl; + u32_t sum = 0, tmp; + /* starts at odd byte address? */ + int odd = ((mem_ptr_t)pb & 1); + + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + ps = (u16_t *)pb; + + if (((mem_ptr_t)ps & 3) && len > 1) { + sum += *ps++; + len -= 2; + } + + pl = (u32_t *)ps; + + while (len > 7) { + tmp = sum + *pl++; /* ping */ + if (tmp < sum) { + tmp++; /* add back carry */ + } + + sum = tmp + *pl++; /* pong */ + if (sum < tmp) { + sum++; /* add back carry */ + } + + len -= 8; + } + + /* make room in upper bits */ + sum = FOLD_U32T(sum); + + ps = (u16_t *)pl; + + /* 16-bit aligned word remaining? */ + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* dangling tail byte remaining? */ + if (len > 0) { /* include odd byte */ + ((u8_t *)&t)[0] = *(u8_t *)ps; + } + + sum += t; /* add end bytes */ + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return (u16_t)sum; +} +#endif + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo(struct pbuf *p, + ip_addr_t *src, ip_addr_t *dest, + u8_t proto, u16_t proto_len) +{ + u32_t acc; + u32_t addr; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + /* iterate through all pbuf in chain */ + for(q = p; q != NULL; q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + acc += LWIP_CHKSUM(q->payload, q->len); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* just executing this next line is probably faster that the if statement needed + to check whether we really need to execute it, and does no harm */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + addr = ip4_addr_get_u32(src); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo_partial(struct pbuf *p, + ip_addr_t *src, ip_addr_t *dest, + u8_t proto, u16_t proto_len, u16_t chksum_len) +{ + u32_t acc; + u32_t addr; + struct pbuf *q; + u8_t swapped; + u16_t chklen; + + acc = 0; + swapped = 0; + /* iterate through all pbuf in chain */ + for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + chklen = q->len; + if (chklen > chksum_len) { + chklen = chksum_len; + } + acc += LWIP_CHKSUM(q->payload, chklen); + chksum_len -= chklen; + LWIP_ASSERT("delete me", chksum_len < 0x7fff); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* fold the upper bit down */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + addr = ip4_addr_get_u32(src); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarily for IP + * and ICMP. + * + * @param dataptr start of the buffer to calculate the checksum (no alignment needed) + * @param len length of the buffer to calculate the checksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ + +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + return ~LWIP_CHKSUM(dataptr, len); +} + +/** + * Calculate a checksum over a chain of pbufs (without pseudo-header, much like + * inet_chksum only pbufs are used). + * + * @param p pbuf chain over that the checksum should be calculated + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pbuf(struct pbuf *p) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += LWIP_CHKSUM(q->payload, q->len); + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + return (u16_t)~(acc & 0xffffUL); +} + +/* These are some implementations for LWIP_CHKSUM_COPY, which copies data + * like MEMCPY but generates a checksum at the same time. Since this is a + * performance-sensitive function, you might want to create your own version + * in assembly targeted at your hardware by defining it in lwipopts.h: + * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len) + */ + +#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */ +/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM. + * For architectures with big caches, data might still be in cache when + * generating the checksum after copying. + */ +u16_t +lwip_chksum_copy(void *dst, const void *src, u16_t len) +{ + MEMCPY(dst, src, len); + return LWIP_CHKSUM(dst, len); +} +#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */ diff --git a/app/lwip/core/ipv4/ip.c b/app/lwip/core/ipv4/ip.c new file mode 100644 index 00000000..6046e342 --- /dev/null +++ b/app/lwip/core/ipv4/ip.c @@ -0,0 +1,904 @@ +/** + * @file + * This is the IPv4 layer implementation for incoming and outgoing IP traffic. + * + * @see ip_frag.c + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip_frag.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/igmp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/stats.h" +#include "arch/perf.h" + +#include + +/** Set this to 0 in the rare case of wanting to call an extra function to + * generate the IP checksum (in contrast to calculating it on-the-fly). */ +#ifndef LWIP_INLINE_IP_CHKSUM +#define LWIP_INLINE_IP_CHKSUM 1 +#endif +#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP +#define CHECKSUM_GEN_IP_INLINE 1 +#else +#define CHECKSUM_GEN_IP_INLINE 0 +#endif + +#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT) +#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1 + +/** Some defines for DHCP to let link-layer-addressed packets through while the + * netif is down. + * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT + * to return 1 if the port is accepted and 0 if the port is not accepted. + */ +#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) +/* accept DHCP client port and custom port */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \ + || (LWIP_IP_ACCEPT_UDP_PORT(port))) +#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ +/* accept custom port only */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(dst_port)) +#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ +/* accept DHCP client port only */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT)) +#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ + +#else /* LWIP_DHCP */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0 +#endif /* LWIP_DHCP */ + +/** + * The interface that provided the packet for the current callback + * invocation. + */ +struct netif *current_netif; + +/** + * Header of the input packet currently being processed. + */ +const struct ip_hdr *current_header; +/** Source IP address of current_header */ +ip_addr_t current_iphdr_src; +/** Destination IP address of current_header */ +ip_addr_t current_iphdr_dest; + +/** The IP header ID of the next outgoing IP packet */ +static u16_t ip_id; + +/** + * Finds the appropriate network interface for a given IP address. It + * searches the list of network interfaces linearly. A match is found + * if the masked IP address of the network interface equals the masked + * IP address given to the function. + * + * @param dest the destination IP address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip_route(ip_addr_t *dest) +{ + struct netif *netif; + + /* iterate through netifs */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + /* network mask matches? */ + if (netif_is_up(netif)) { + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + /* return netif on which to forward IP packet */ + return netif; + } + if (!ip_addr_isbroadcast(dest, netif) && netif != netif_default) { + return netif; + } + } + } + if ((netif_default == NULL) || (!netif_is_up(netif_default))) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + snmp_inc_ipoutnoroutes(); + return NULL; + } + /* no matching netif found, use default netif */ + return netif_default; +} + +/** + * Finds the appropriate network interface for a source IP address. It + * searches the list of network interfaces linearly. A match is found + * if the masked IP address of the network interface equals the masked + * IP address given to the function. + * + * @param source the sourcination IP address for which to find the route + * @return the netif on which to send to reach source + */ + +struct netif *ICACHE_FLASH_ATTR +ip_router(ip_addr_t *dest, ip_addr_t *source){ + struct netif *netif; + /* iterate through netifs */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + /* network mask matches? */ + + if (netif_is_up(netif)) { + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + + if (netif_is_up(netif)) { + if (ip_addr_netcmp(source, &(netif->ip_addr), &(netif->netmask))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + } + + if ((netif_default == NULL) || (!netif_is_up(netif_default))) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + snmp_inc_ipoutnoroutes(); + return NULL; + } + /* no matching netif found, use default netif */ + os_printf("ip_router %d %p\n", __LINE__, netif_default); + return netif_default; +} + +#if IP_FORWARD +/** + * Forwards an IP packet. It finds an appropriate route for the + * packet, decrements the TTL value of the packet, adjusts the + * checksum and outputs the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IP header of the input packet + * @param inp the netif on which this packet was received + */ +static void ICACHE_FLASH_ATTR +ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + PERF_START; + + /* RFC3927 2.7: do not forward link-local addresses */ + if (ip_addr_islinklocal(¤t_iphdr_dest)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest), + ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest))); + goto return_noroute; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip_route(¤t_iphdr_dest); + if (netif == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n", + ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest), + ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest))); + goto return_noroute; + } + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n")); + goto return_noroute; + } + + /* decrement TTL */ + IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); + /* send ICMP if TTL == 0 */ + if (IPH_TTL(iphdr) == 0) { + snmp_inc_ipinhdrerrors(); +#if LWIP_ICMP + /* Don't send ICMP messages in response to ICMP messages */ + if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) { + icmp_time_exceeded(p, ICMP_TE_TTL); + } +#endif /* LWIP_ICMP */ + return; + } + + /* Incrementally update the IP checksum. */ + if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffff - 0x100)) { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1); + } else { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100)); + } + + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest), + ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest))); + + IP_STATS_INC(ip.fw); + IP_STATS_INC(ip.xmit); + snmp_inc_ipforwdatagrams(); + + PERF_STOP("ip_forward"); + /* transmit pbuf on chosen interface */ + netif->output(netif, p, ¤t_iphdr_dest); + return; +return_noroute: + snmp_inc_ipoutnoroutes(); +} +#endif /* IP_FORWARD */ + +/** + * This function is called by the network interface device driver when + * an IP packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip_forward). The IP checksum is always checked. + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IP packet (p->payload points to IP header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) + */ +err_t +ip_input(struct pbuf *p, struct netif *inp) +{ + struct ip_hdr *iphdr; + struct netif *netif; + u16_t iphdr_hlen; + u16_t iphdr_len; +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + int check_ip_src=1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + IP_STATS_INC(ip.recv); + snmp_inc_ipinreceives(); + + /* identify the IP header */ + iphdr = (struct ip_hdr *)p->payload; + if (IPH_V(iphdr) != 4) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.err); + IP_STATS_INC(ip.drop); + snmp_inc_ipinhdrerrors(); + return ERR_OK; + } + + /* obtain IP header length in number of 32-bit words */ + iphdr_hlen = IPH_HL(iphdr); + /* calculate IP header length in bytes */ + iphdr_hlen *= 4; + /* obtain ip length in bytes */ + iphdr_len = ntohs(IPH_LEN(iphdr)); + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) { + if (iphdr_hlen > p->len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_hlen, p->len)); + } + if (iphdr_len > p->tot_len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_len, p->tot_len)); + } + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.lenerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipindiscards(); + return ERR_OK; + } + + /* verify checksum */ +#if CHECKSUM_CHECK_IP + if (inet_chksum(iphdr, iphdr_hlen) != 0) { + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.chkerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinhdrerrors(); + return ERR_OK; + } +#endif + + /* Trim pbuf. This should have been done at the netif layer, + * but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, iphdr_len); + + /* copy IP addresses to aligned ip_addr_t */ + ip_addr_copy(current_iphdr_dest, iphdr->dest); + ip_addr_copy(current_iphdr_src, iphdr->src); + + /* match packet against an interface, i.e. is this packet for us? */ +#if LWIP_IGMP + if (ip_addr_ismulticast(¤t_iphdr_dest)) { + if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ¤t_iphdr_dest))) { + netif = inp; + } else { + netif = NULL; + } + } else +#endif /* LWIP_IGMP */ + { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n", + ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(&netif->ip_addr), + ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(&netif->netmask), + ip4_addr_get_u32(&netif->ip_addr) & ip4_addr_get_u32(&netif->netmask), + ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(&netif->netmask))); + + /* interface is up and configured? */ + if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) { + /* unicast to this interface address? */ + if (ip_addr_cmp(¤t_iphdr_dest, &(netif->ip_addr)) || + /* or broadcast on this interface network address? */ + ip_addr_isbroadcast(¤t_iphdr_dest, netif)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } +#if LWIP_AUTOIP + /* connections to link-local addresses must persist after changing + the netif's address (RFC3927 ch. 1.9) */ + if ((netif->autoip != NULL) && + ip_addr_cmp(¤t_iphdr_dest, &(netif->autoip->llipaddr))) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } +#endif /* LWIP_AUTOIP */ + } + if (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); + } + +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed + * using link layer addressing (such as Ethernet MAC) so we must not filter on IP. + * According to RFC 1542 section 3.1.1, referred by RFC 2131). + * + * If you want to accept private broadcast communication while a netif is down, + * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.: + * + * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345)) + */ + if (netif == NULL) { + /* remote port is DHCP server? */ + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n", + ntohs(udphdr->dest))); + if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n")); + netif = inp; + check_ip_src = 0; + } + } + } +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */ +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */ + if (check_ip_src && !ip_addr_isany(¤t_iphdr_src)) +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + { if ((ip_addr_isbroadcast(¤t_iphdr_src, inp)) || + (ip_addr_ismulticast(¤t_iphdr_src))) { + /* packet source is not valid */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n")); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.drop); + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + return ERR_OK; + } + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n")); +#if IP_FORWARD + /* non-broadcast packet? */ + if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp)) { + /* try to forward IP packet on (other) interfaces */ + ip_forward(p, iphdr, inp); + } else +#endif /* IP_FORWARD */ + { + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + } + pbuf_free(p); + return ERR_OK; + } + /* packet consists of multiple fragments? */ + if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) { +#if IP_REASSEMBLY /* packet fragment reassembly code present? */ + LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n", + ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8)); + /* reassemble the packet*/ + p = ip_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + return ERR_OK; + } + iphdr = (struct ip_hdr *)p->payload; +#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ + pbuf_free(p); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", + ntohs(IPH_OFFSET(iphdr)))); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); + return ERR_OK; +#endif /* IP_REASSEMBLY */ + } + +#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */ + +#if LWIP_IGMP + /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */ + if((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) { +#else + if (iphdr_hlen > IP_HLEN) { +#endif /* LWIP_IGMP */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n")); + pbuf_free(p); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); + return ERR_OK; + } +#endif /* IP_OPTIONS_ALLOWED == 0 */ + + /* send to upper layers */ + LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n")); + ip_debug_print(p); + LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + + current_netif = inp; + current_header = iphdr; + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + + switch (IPH_PROTO(iphdr)) { +#if LWIP_UDP + case IP_PROTO_UDP: +#if LWIP_UDPLITE + case IP_PROTO_UDPLITE: +#endif /* LWIP_UDPLITE */ + snmp_inc_ipindelivers(); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP_PROTO_TCP: + snmp_inc_ipindelivers(); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP + case IP_PROTO_ICMP: + snmp_inc_ipindelivers(); + icmp_input(p, inp); + break; +#endif /* LWIP_ICMP */ +#if LWIP_IGMP + case IP_PROTO_IGMP: + igmp_input(p, inp, ¤t_iphdr_dest); + break; +#endif /* LWIP_IGMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable unless is was a broadcast */ + if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp) && + !ip_addr_ismulticast(¤t_iphdr_dest)) { + p->payload = iphdr; + icmp_dest_unreach(p, ICMP_DUR_PROTO); + } +#endif /* LWIP_ICMP */ + pbuf_free(p); + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr))); + + IP_STATS_INC(ip.proterr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinunknownprotos(); + } + } + + current_netif = NULL; + current_header = NULL; + ip_addr_set_any(¤t_iphdr_src); + ip_addr_set_any(¤t_iphdr_dest); + + return ERR_OK; +} + +/** + * Sends an IP packet on a network interface. This function constructs + * the IP header and calculates the IP header checksum. If the source + * IP address is NULL, the IP address of the outgoing network + * interface is filled in as source address. + * If the destination IP address is IP_HDRINCL, p is assumed to already + * include an IP header and p->payload points to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + * + * @note ip_id: RFC791 "some host may be able to simply use + * unique identifiers independent of destination" + */ +err_t +ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, + u8_t proto, struct netif *netif) +{ +#if IP_OPTIONS_SEND + return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0); +} + +/** + * Same as ip_output_if() but with the possibility to include IP options: + * + * @ param ip_options pointer to the IP options, copied into the IP header + * @ param optlen length of ip_options + */ +err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen) +{ +#endif /* IP_OPTIONS_SEND */ + struct ip_hdr *iphdr; + ip_addr_t dest_addr; +#if CHECKSUM_GEN_IP_INLINE + u32_t chk_sum = 0; +#endif /* CHECKSUM_GEN_IP_INLINE */ + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + snmp_inc_ipoutrequests(); + + /* Should the IP header be generated or is it already included in p? */ + if (dest != IP_HDRINCL) { + u16_t ip_hlen = IP_HLEN; +#if IP_OPTIONS_SEND + u16_t optlen_aligned = 0; + if (optlen != 0) { +#if CHECKSUM_GEN_IP_INLINE + int i; +#endif /* CHECKSUM_GEN_IP_INLINE */ + /* round up to a multiple of 4 */ + optlen_aligned = ((optlen + 3) & ~3); + ip_hlen += optlen_aligned; + /* First write in the IP options */ + if (pbuf_header(p, optlen_aligned)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n")); + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + MEMCPY(p->payload, ip_options, optlen); + if (optlen < optlen_aligned) { + /* zero the remaining bytes */ + memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen); + } +#if CHECKSUM_GEN_IP_INLINE + for (i = 0; i < optlen_aligned/2; i++) { + chk_sum += ((u16_t*)p->payload)[i]; + } +#endif /* CHECKSUM_GEN_IP_INLINE */ + } +#endif /* IP_OPTIONS_SEND */ + /* generate IP header */ + if (pbuf_header(p, IP_HLEN)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n")); + + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + + iphdr = (struct ip_hdr *)p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip_hdr", + (p->len >= sizeof(struct ip_hdr))); + + IPH_TTL_SET(iphdr, ttl); + IPH_PROTO_SET(iphdr, proto); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += LWIP_MAKE_U16(proto, ttl); +#endif /* CHECKSUM_GEN_IP_INLINE */ + + /* dest cannot be NULL here */ + ip_addr_copy(iphdr->dest, *dest); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF; + chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16; +#endif /* CHECKSUM_GEN_IP_INLINE */ + + IPH_VHLTOS_SET(iphdr, 4, ip_hlen / 4, tos); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_v_hl_tos; +#endif /* CHECKSUM_GEN_IP_INLINE */ + IPH_LEN_SET(iphdr, htons(p->tot_len)); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_len; +#endif /* CHECKSUM_GEN_IP_INLINE */ + IPH_OFFSET_SET(iphdr, 0); + IPH_ID_SET(iphdr, htons(ip_id)); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_id; +#endif /* CHECKSUM_GEN_IP_INLINE */ + ++ip_id; + + if (ip_addr_isany(src)) { + ip_addr_copy(iphdr->src, netif->ip_addr); + } else { + /* src cannot be NULL here */ + ip_addr_copy(iphdr->src, *src); + } + +#if CHECKSUM_GEN_IP_INLINE + chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF; + chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16; + chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF); + chk_sum = (chk_sum >> 16) + chk_sum; + chk_sum = ~chk_sum; + iphdr->_chksum = chk_sum; /* network order */ +#else /* CHECKSUM_GEN_IP_INLINE */ + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen)); +#endif +#endif /* CHECKSUM_GEN_IP_INLINE */ + } else { + /* IP header already included in p */ + iphdr = (struct ip_hdr *)p->payload; + ip_addr_copy(dest_addr, iphdr->dest); + dest = &dest_addr; + } + + IP_STATS_INC(ip.xmit); + + LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip_debug_print(p); + +#if ENABLE_LOOPBACK + if (ip_addr_cmp(dest, &netif->ip_addr)) { + /* Packet to self, enqueue it for loopback */ + LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()")); + return netif_loop_output(netif, p, dest); + } +#if LWIP_IGMP + if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) { + netif_loop_output(netif, p, dest); + } +#endif /* LWIP_IGMP */ +#endif /* ENABLE_LOOPBACK */ +#if IP_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + return ip_frag(p, netif, dest); + } +#endif /* IP_FRAG */ + + LWIP_DEBUGF(IP_DEBUG, ("netif->output()\n")); + return netif->output(netif, p, dest); +} + +/** + * Simple interface to ip_output_if. It finds the outgoing network + * interface and calls upon ip_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto) +{ + struct netif *netif; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + return ip_output_if(p, src, dest, ttl, tos, proto, netif); +} + +#if LWIP_NETIF_HWADDRHINT +/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip_output_if. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param addr_hint address hint pointer set to netif->addr_hint before + * calling ip_output_if() + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint) +{ + struct netif *netif; + err_t err; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + netif->addr_hint = addr_hint; + err = ip_output_if(p, src, dest, ttl, tos, proto, netif); + netif->addr_hint = NULL; + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if IP_DEBUG +/* Print an IP header by using LWIP_DEBUGF + * @param p an IP packet, p->payload pointing to the IP header + */ +void +ip_debug_print(struct pbuf *p) +{ + struct ip_hdr *iphdr = (struct ip_hdr *)p->payload; + u8_t *payload; + + payload = (u8_t *)iphdr + IP_HLEN; + + LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n", + IPH_V(iphdr), + IPH_HL(iphdr), + IPH_TOS(iphdr), + ntohs(IPH_LEN(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n", + ntohs(IPH_ID(iphdr)), + ntohs(IPH_OFFSET(iphdr)) >> 15 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 14 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 13 & 1, + ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n", + IPH_TTL(iphdr), + IPH_PROTO(iphdr), + ntohs(IPH_CHKSUM(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n", + ip4_addr1_16(&iphdr->src), + ip4_addr2_16(&iphdr->src), + ip4_addr3_16(&iphdr->src), + ip4_addr4_16(&iphdr->src))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n", + ip4_addr1_16(&iphdr->dest), + ip4_addr2_16(&iphdr->dest), + ip4_addr3_16(&iphdr->dest), + ip4_addr4_16(&iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ diff --git a/app/lwip/core/ipv4/ip_addr.c b/app/lwip/core/ipv4/ip_addr.c new file mode 100644 index 00000000..b39ed7c8 --- /dev/null +++ b/app/lwip/core/ipv4/ip_addr.c @@ -0,0 +1,312 @@ +/** + * @file + * This is the IPv4 address tools implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ +const ip_addr_t ip_addr_any = { IPADDR_ANY }; +const ip_addr_t ip_addr_broadcast = { IPADDR_BROADCAST }; + +/** + * Determine if an address is a broadcast address on a network interface + * + * @param addr address to be checked + * @param netif the network interface against which the address is checked + * @return returns non-zero if the address is a broadcast address + */ +u8_t +ip4_addr_isbroadcast(u32_t addr, const struct netif *netif) +{ + ip_addr_t ipaddr; + ip4_addr_set_u32(&ipaddr, addr); + + /* all ones (broadcast) or all zeroes (old skool broadcast) */ + if ((~addr == IPADDR_ANY) || + (addr == IPADDR_ANY)) { + return 1; + /* no broadcast support on this network interface? */ + } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) { + /* the given address cannot be a broadcast address + * nor can we check against any broadcast addresses */ + return 0; + /* address matches network interface address exactly? => no broadcast */ + } else if (addr == ip4_addr_get_u32(&netif->ip_addr)) { + return 0; + /* on the same (sub) network... */ + } else if (ip_addr_netcmp(&ipaddr, &(netif->ip_addr), &(netif->netmask)) + /* ...and host identifier bits are all ones? =>... */ + && ((addr & ~ip4_addr_get_u32(&netif->netmask)) == + (IPADDR_BROADCAST & ~ip4_addr_get_u32(&netif->netmask)))) { + /* => network broadcast address */ + return 1; + } else { + return 0; + } +} + +/** Checks if a netmask is valid (starting with ones, then only zeros) + * + * @param netmask the IPv4 netmask to check (in network byte order!) + * @return 1 if the netmask is valid, 0 if it is not + */ +u8_t +ip4_addr_netmask_valid(u32_t netmask) +{ + u32_t mask; + u32_t nm_hostorder = lwip_htonl(netmask); + + /* first, check for the first zero */ + for (mask = 1U << 31 ; mask != 0; mask >>= 1) { + if ((nm_hostorder & mask) == 0) { + break; + } + } + /* then check that there is no one */ + for (; mask != 0; mask >>= 1) { + if ((nm_hostorder & mask) != 0) { + /* there is a one after the first zero -> invalid */ + return 0; + } + } + /* no one after the first zero -> valid */ + return 1; +} + +/* Here for now until needed in other places in lwIP */ +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +//#define isdigit(c) in_range(c, '0', '9') +//#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#endif + +/** + * Ascii internet address interpretation routine. + * The value returned is in network order. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @return ip address in network order + */ +u32_t +ipaddr_addr(const char *cp) +{ + ip_addr_t val; + + if (ipaddr_aton(cp, &val)) { + return ip4_addr_get_u32(&val); + } + return (IPADDR_NONE); +} + +/** + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +ipaddr_aton(const char *cp, ip_addr_t *addr) +{ + u32_t val; + u8_t base; + char c; + u32_t parts[4]; + u32_t *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, 1-9=decimal. + */ + if (!isdigit(c)) + return (0); + val = 0; + base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') { + base = 16; + c = *++cp; + } else + base = 8; + } + for (;;) { + if (isdigit(c)) { + val = (val * base) + (int)(c - '0'); + c = *++cp; + } else if (base == 16 && isxdigit(c)) { + val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) { + return (0); + } + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && !isspace(c)) { + return (0); + } + /* + * Concoct the address according to + * the number of parts specified. + */ + switch (pp - parts + 1) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffffUL) { + return (0); + } + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) { + return (0); + } + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) { + return (0); + } + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + default: + LWIP_ASSERT("unhandled", 0); + break; + } + if (addr) { + ip4_addr_set_u32(addr, htonl(val)); + } + return (1); +} + +/** + * Convert numeric IP address into decimal dotted ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * represenation of addr + */ +char * +ipaddr_ntoa(const ip_addr_t *addr) +{ + static char str[16]; + return ipaddr_ntoa_r(addr, str, 16); +} + +/** + * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. + * + * @param addr ip address in network order to convert + * @param buf target buffer where the string is stored + * @param buflen length of buf + * @return either pointer to buf which now holds the ASCII + * representation of addr or NULL if buf was too small + */ +char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen) +{ + u32_t s_addr; + char inv[3]; + char *rp; + u8_t *ap; + u8_t rem; + u8_t n; + u8_t i; + int len = 0; + + s_addr = ip4_addr_get_u32(addr); + + rp = buf; + ap = (u8_t *)&s_addr; + for(n = 0; n < 4; n++) { + i = 0; + do { + rem = *ap % (u8_t)10; + *ap /= (u8_t)10; + inv[i++] = '0' + rem; + } while(*ap); + while(i--) { + if (len++ >= buflen) { + return NULL; + } + *rp++ = inv[i]; + } + if (len++ >= buflen) { + return NULL; + } + *rp++ = '.'; + ap++; + } + *--rp = 0; + return buf; +} diff --git a/app/lwip/core/ipv4/ip_frag.c b/app/lwip/core/ipv4/ip_frag.c new file mode 100644 index 00000000..5eff96f2 --- /dev/null +++ b/app/lwip/core/ipv4/ip_frag.c @@ -0,0 +1,863 @@ +/** + * @file + * This is the IPv4 packet segmentation and reassembly implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * Simon Goldschmidt + * original reassembly code by Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_frag.h" +#include "lwip/def.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/stats.h" +#include "lwip/icmp.h" + +#include + +#if IP_REASSEMBLY +/** + * The IP reassembly code currently has the following limitations: + * - IP header options are not supported + * - fragments must not overlap (e.g. due to different routes), + * currently, overlapping or duplicate fragments are thrown away + * if IP_REASS_CHECK_OVERLAP=1 (the default)! + * + * @todo: work with IP header options + */ + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has the same packing requirements as the IP header, since it replaces + * the IP header in memory in incoming fragments (after copying it) to keep + * track of the various fragments. (-> If the IP header doesn't need packing, + * this struct doesn't need packing, too.) + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \ + (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \ + ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \ + IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0 + +/* global variables */ +static struct ip_reassdata *reassdatagrams; +static u16_t ip_reass_pbufcount; + +/* function prototypes */ +static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)ICACHE_FLASH_ATTR; +static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)ICACHE_FLASH_ATTR; + +/** + * Reassembly timer base function + * for both NO_SYS == 0 and 1 (!). + * + * Should be called every 1000 msec (defined by IP_TMR_INTERVAL). + */ +void +ip_reass_tmr(void) +{ + struct ip_reassdata *r, *prev = NULL; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer)); + prev = r; + r = r->next; + } else { + /* reassembly timed out */ + struct ip_reassdata *tmp; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n")); + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip_reass_free_complete_datagram(tmp, prev); + } + } +} + +/** + * Free a datagram (struct ip_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip_reass_pbufcount), + * SNMP counters and sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + * @param prev the previous datagram in the linked list + * @return the number of pbufs freed + */ +static int +ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + u16_t pbufs_freed = 0; + u8_t clen; + struct pbuf *p; + struct ip_reass_helper *iprh; + + LWIP_ASSERT("prev != ipr", prev != ipr); + if (prev != NULL) { + LWIP_ASSERT("prev->next == ipr", prev->next == ipr); + } + + snmp_inc_ipreasmfails(); +#if LWIP_ICMP + iprh = (struct ip_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, copy the original header into it. */ + SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); + icmp_time_exceeded(p, ICMP_TE_FRAG); + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + clen = pbuf_clen(pcur); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(pcur); + } + /* Then, unchain the struct ip_reassdata from the list and free it. */ + ip_reass_dequeue_datagram(ipr, prev); + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); + ip_reass_pbufcount -= pbufs_freed; + + return pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram 'fraghdr' belongs to is not freed! + * + * @param fraghdr IP header of the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + * @return the number of pbufs freed + */ +static int +ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) +{ + /* @todo Can't we simply remove the last datagram in the + * linked list behind reassdatagrams? + */ + struct ip_reassdata *r, *oldest, *prev; + int pbufs_freed = 0, pbufs_freed_current; + int other_datagrams; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the datagram that 'fraghdr' belongs to! */ + do { + oldest = NULL; + prev = NULL; + other_datagrams = 0; + r = reassdatagrams; + while (r != NULL) { + if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { + /* Not the same datagram as fraghdr */ + other_datagrams++; + if (oldest == NULL) { + oldest = r; + } else if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + if (r->next != NULL) { + prev = r; + } + r = r->next; + } + if (oldest != NULL) { + pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev); + pbufs_freed += pbufs_freed_current; + } + } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); + return pbufs_freed; +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Enqueues a new fragment into the fragment queue + * @param fraghdr points to the new fragments IP hdr + * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space) + * @return A pointer to the queue location into which the fragment was enqueued + */ +static struct ip_reassdata* ICACHE_FLASH_ATTR +ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen) +{ + struct ip_reassdata* ipr; + /* No matching previous fragment found, allocate a new reassdata struct */ + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + } + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IPFRAG_STATS_INC(ip_frag.memerr); + LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n")); + return NULL; + } + } + memset(ipr, 0, sizeof(struct ip_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + /* copy the ip header for later tests and input */ + /* @todo: no ip options supported? */ + SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN); + return ipr; +} + +/** + * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs. + * @param ipr points to the queue entry to dequeue + */ +static void ICACHE_FLASH_ATTR +ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + + /* dequeue the reass struct */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", prev != NULL); + prev->next = ipr->next; + } + + /* now we can free the ip_reass struct */ + memp_free(MEMP_REASSDATA, ipr); +} + +/** + * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list + * will grow over time as new pbufs are rx. + * Also checks that the datagram passes basic continuity checks (if the last + * fragment was received at least once). + * @param root_p points to the 'root' pbuf for the current datagram being assembled. + * @param new_p points to the pbuf for the current fragment + * @return 0 if invalid, >0 otherwise + */ +static int ICACHE_FLASH_ATTR +ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p) +{ + struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct pbuf *q; + u16_t offset,len; + struct ip_hdr *fraghdr; + int valid = 1; + + /* Extract length and fragment offset from current fragment */ + fraghdr = (struct ip_hdr*)new_p->payload; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + + /* overwrite the fragment's ip header from the pbuf with our helper struct, + * and setup the embedded helper structure. */ + /* make sure the struct ip_reass_helper fits into the IP header */ + LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN", + sizeof(struct ip_reass_helper) <= IP_HLEN); + iprh = (struct ip_reass_helper*)new_p->payload; + iprh->next_pbuf = NULL; + iprh->start = offset; + iprh->end = offset + len; + + /* Iterate through until we either get to the end of the list (append), + * or we find on with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ +#if IP_REASS_CHECK_OVERLAP + if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) { + /* fragment overlaps with previous or following, throw away */ + goto freepbuf; + } +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + } else { + /* fragment with the lowest offset */ + ipr->p = new_p; + } + break; + } else if(iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + goto freepbuf; +#if IP_REASS_CHECK_OVERLAP + } else if(iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + goto freepbuf; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no wholes. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = new_p; + } + } + + /* At this point, the validation part begins: */ + /* If we already received the last fragment */ + if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) { + /* and had no wholes so far */ + if (valid) { + /* then check if the rest of the fragments is here */ + /* Check if the queue starts with the first datagram */ + if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) { + valid = 0; + } else { + /* and check that there are no wholes after this datagram */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while (q != NULL) { + iprh = (struct ip_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + /* if still valid, all fragments are received + * (because to the MF==0 already arrived */ + if (valid) { + LWIP_ASSERT("sanity check", ipr->p != NULL); + LWIP_ASSERT("sanity check", + ((struct ip_reass_helper*)ipr->p->payload) != iprh); + LWIP_ASSERT("validate_datagram:next_pbuf!=NULL", + iprh->next_pbuf == NULL); + LWIP_ASSERT("validate_datagram:datagram end!=datagram len", + iprh->end == ipr->datagram_len); + } + } + } + /* If valid is 0 here, there are some fragments missing in the middle + * (since MF == 0 has already arrived). Such datagrams simply time out if + * no more fragments are received... */ + return valid; + } + /* If we come here, not all fragments were received, yet! */ + return 0; /* not yet valid! */ +#if IP_REASS_CHECK_OVERLAP +freepbuf: + ip_reass_pbufcount -= pbuf_clen(new_p); + pbuf_free(new_p); + return 0; +#endif /* IP_REASS_CHECK_OVERLAP */ +} + +/** + * Reassembles incoming IP fragments into an IP datagram. + * + * @param p points to a pbuf chain of the fragment + * @return NULL if reassembly is incomplete, ? otherwise + */ +struct pbuf * +ip_reass(struct pbuf *p) +{ + struct pbuf *r; + struct ip_hdr *fraghdr; + struct ip_reassdata *ipr; + struct ip_reass_helper *iprh; + u16_t offset, len; + u8_t clen; + struct ip_reassdata *ipr_prev = NULL; + + IPFRAG_STATS_INC(ip_frag.recv); + snmp_inc_ipreasmreqds(); + + fraghdr = (struct ip_hdr*)p->payload; + + if ((IPH_HL(fraghdr) * 4) != IP_HLEN) { + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n")); + IPFRAG_STATS_INC(ip_frag.err); + goto nullreturn; + } + + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + + /* Check if we are allowed to enqueue more datagrams. */ + clen = pbuf_clen(p); + if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || + ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* No datagram could be freed and still too many pbufs enqueued */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n", + ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS)); + IPFRAG_STATS_INC(ip_frag.memerr); + /* @todo: send ICMP time exceeded here? */ + /* drop this pbuf */ + goto nullreturn; + } + } + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n", + ntohs(IPH_ID(fraghdr)))); + IPFRAG_STATS_INC(ip_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); + /* Bail if unable to enqueue */ + if(ipr == NULL) { + goto nullreturn; + } + } else { + if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && + ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { + /* ipr->iphdr is not the header from the first fragment, but fraghdr is + * -> copy fraghdr into ipr->iphdr since we want to have the header + * of the first fragment (for ICMP time exceeded and later, for copying + * all options, if supported)*/ + SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); + } + } + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip_reass_pbufcount += clen; + + /* At this point, we have either created a new entry or pointing + * to an existing one */ + + /* check for 'no more fragments', and update queue entry*/ + if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) { + ipr->flags |= IP_REASS_FLAG_LASTFRAG; + ipr->datagram_len = offset + len; + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: last fragment seen, total len %"S16_F"\n", + ipr->datagram_len)); + } + /* find the right place to insert this pbuf */ + /* @todo: trim pbufs if fragments are overlapping */ + if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) { + /* the totally last fragment (flag more fragments = 0) was received at least + * once AND all fragments are received */ + ipr->datagram_len += IP_HLEN; + + /* save the second pbuf before copying the header over the pointer */ + r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; + + /* copy the original ip header back to the first pbuf */ + fraghdr = (struct ip_hdr*)(ipr->p->payload); + SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); + IPH_LEN_SET(fraghdr, htons(ipr->datagram_len)); + IPH_OFFSET_SET(fraghdr, 0); + IPH_CHKSUM_SET(fraghdr, 0); + /* @todo: do we need to set calculate the correct checksum? */ + IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); + + p = ipr->p; + + /* chain together the pbufs contained within the reass_data list. */ + while(r != NULL) { + iprh = (struct ip_reass_helper*)r->payload; + + /* hide the ip header for every succeding fragment */ + pbuf_header(r, -IP_HLEN); + pbuf_cat(p, r); + r = iprh->next_pbuf; + } + /* release the sources allocate for the fragment queue entry */ + ip_reass_dequeue_datagram(ipr, ipr_prev); + + /* and adjust the number of pbufs currently queued for reassembly. */ + ip_reass_pbufcount -= pbuf_clen(p); + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); + return NULL; + +nullreturn: + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n")); + IPFRAG_STATS_INC(ip_frag.drop); + pbuf_free(p); + return NULL; +} +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if IP_FRAG_USES_STATIC_BUF +static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)]; +#else /* IP_FRAG_USES_STATIC_BUF */ + +#if !LWIP_NETIF_TX_SINGLE_PBUF +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* ICACHE_FLASH_ATTR +ip_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void ICACHE_FLASH_ATTR +ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) +{ + LWIP_ASSERT("p != NULL", p != NULL); + memp_free(MEMP_FRAG_PBUF, p); +} + +/** Free-callback function to free a 'struct pbuf_custom_ref', called by + * pbuf_free. */ +static void ICACHE_FLASH_ATTR +ipfrag_free_pbuf_custom(struct pbuf *p) +{ + struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; + LWIP_ASSERT("pcr != NULL", pcr != NULL); + LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); + if (pcr->original != NULL) { + pbuf_free(pcr->original); + } + ip_frag_free_pbuf_custom_ref(pcr); +} +#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */ +#endif /* IP_FRAG_USES_STATIC_BUF */ + +/** + * Fragment an IP datagram if too large for the netif. + * + * Chop the datagram in MTU sized chunks and send them in order + * by using a fixed size static memory buffer (PBUF_REF) or + * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF). + * + * @param p ip packet to send + * @param netif the netif on which to send + * @param dest destination ip address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest) +{ + struct pbuf *rambuf; +#if IP_FRAG_USES_STATIC_BUF + struct pbuf *header; +#else +#if !LWIP_NETIF_TX_SINGLE_PBUF + struct pbuf *newpbuf; +#endif + struct ip_hdr *original_iphdr; +#endif + struct ip_hdr *iphdr; + u16_t nfb; + u16_t left, cop; + u16_t mtu = netif->mtu; + u16_t ofo, omf; + u16_t last; + u16_t poff = IP_HLEN; + u16_t tmp; +#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF + u16_t newpbuflen = 0; + u16_t left_to_copy; +#endif + + /* Get a RAM based MTU sized pbuf */ +#if IP_FRAG_USES_STATIC_BUF + /* When using a static buffer, we use a PBUF_REF, which we will + * use to reference the packet (without link header). + * Layer and length is irrelevant. + */ + rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF); + if (rambuf == NULL) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n")); + return ERR_MEM; + } + rambuf->tot_len = rambuf->len = mtu; + rambuf->payload = LWIP_MEM_ALIGN((void *)buf); + + /* Copy the IP header in it */ + iphdr = (struct ip_hdr *)rambuf->payload; + SMEMCPY(iphdr, p->payload, IP_HLEN); +#else /* IP_FRAG_USES_STATIC_BUF */ + original_iphdr = (struct ip_hdr *)p->payload; + iphdr = original_iphdr; +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Save original offset */ + tmp = ntohs(IPH_OFFSET(iphdr)); + ofo = tmp & IP_OFFMASK; + omf = tmp & IP_MF; + + left = p->tot_len - IP_HLEN; + + nfb = (mtu - IP_HLEN) / 8; + + while (left) { + last = (left <= mtu - IP_HLEN); + + /* Set new offset and MF flag */ + tmp = omf | (IP_OFFMASK & (ofo)); + if (!last) { + tmp = tmp | IP_MF; + } + + /* Fill this fragment */ + cop = last ? left : nfb * 8; + +#if IP_FRAG_USES_STATIC_BUF + poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff); +#else /* IP_FRAG_USES_STATIC_BUF */ +#if LWIP_NETIF_TX_SINGLE_PBUF + rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM); + if (rambuf == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL)); + poff += pbuf_copy_partial(p, rambuf->payload, cop, poff); + /* make room for the IP header */ + if(pbuf_header(rambuf, IP_HLEN)) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* fill in the IP header */ + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = rambuf->payload; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link and IP header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM); + if (rambuf == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP_HLEN))); + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = (struct ip_hdr *)rambuf->payload; + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + struct pbuf_custom_ref *pcr; + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + pcr = ip_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen); + if (newpbuf == NULL) { + ip_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + return ERR_MEM; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ipfrag_free_pbuf_custom; + + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) { + p = p->next; + } + } + poff = newpbuflen; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Correct header */ + IPH_OFFSET_SET(iphdr, htons(tmp)); + IPH_LEN_SET(iphdr, htons(cop + IP_HLEN)); + IPH_CHKSUM_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + +#if IP_FRAG_USES_STATIC_BUF + if (last) { + pbuf_realloc(rambuf, left + IP_HLEN); + } + + /* This part is ugly: we alloc a RAM based pbuf for + * the link level header for each chunk and then + * free it.A PBUF_ROM style pbuf for which pbuf_header + * worked would make things simpler. + */ + header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM); + if (header != NULL) { + pbuf_chain(header, rambuf); + netif->output(netif, header, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + snmp_inc_ipfragcreates(); + pbuf_free(header); + } else { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n")); + pbuf_free(rambuf); + return ERR_MEM; + } +#else /* IP_FRAG_USES_STATIC_BUF */ + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + netif->output(netif, rambuf, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + left -= cop; + ofo += nfb; + } +#if IP_FRAG_USES_STATIC_BUF + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + snmp_inc_ipfragoks(); + return ERR_OK; +} +#endif /* IP_FRAG */ diff --git a/app/lwip/core/mem.c b/app/lwip/core/mem.c new file mode 100644 index 00000000..9a66f295 --- /dev/null +++ b/app/lwip/core/mem.c @@ -0,0 +1,644 @@ +/** + * @file + * Dynamic memory manager + * + * This is a lightweight replacement for the standard C library malloc(). + * + * If you want to use the standard C library malloc() instead, define + * MEM_LIBC_MALLOC to 1 in your lwipopts.h + * + * To let mem_malloc() use pools (prevents fragmentation and is much faster than + * a heap but might waste some memory), define MEM_USE_POOLS to 1, define + * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list + * of pools like this (more pools can be added between _START and _END): + * + * Define three pools with sizes 256, 512, and 1512 bytes + * LWIP_MALLOC_MEMPOOL_START + * LWIP_MALLOC_MEMPOOL(20, 256) + * LWIP_MALLOC_MEMPOOL(10, 512) + * LWIP_MALLOC_MEMPOOL(5, 1512) + * LWIP_MALLOC_MEMPOOL_END + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "lwip/err.h" + +#include + +#if MEM_USE_POOLS +/* lwIP head implemented with different sized pools */ + +/** + * Allocate memory: determine the smallest pool that is big enough + * to contain an element of 'size' and get an element from that pool. + * + * @param size the size in bytes of the memory needed + * @return a pointer to the allocated memory or NULL if the pool is empty + */ +void * +mem_malloc(mem_size_t size) +{ + struct memp_malloc_helper *element; + memp_t poolnr; + mem_size_t required_size = size + sizeof(struct memp_malloc_helper); + + for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) { +#if MEM_USE_POOLS_TRY_BIGGER_POOL +again: +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + /* is this pool big enough to hold an element of the required size + plus a struct memp_malloc_helper that saves the pool this element came from? */ + if (required_size <= memp_sizes[poolnr]) { + break; + } + } + if (poolnr > MEMP_POOL_LAST) { + LWIP_ASSERT("mem_malloc(): no pool is that big!", 0); + return NULL; + } + element = (struct memp_malloc_helper*)memp_malloc(poolnr); + if (element == NULL) { + /* No need to DEBUGF or ASSERT: This error is already + taken care of in memp.c */ +#if MEM_USE_POOLS_TRY_BIGGER_POOL + /** Try a bigger pool if this one is empty! */ + if (poolnr < MEMP_POOL_LAST) { + poolnr++; + goto again; + } +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + return NULL; + } + + /* save the pool number this element came from */ + element->poolnr = poolnr; + /* and return a pointer to the memory directly after the struct memp_malloc_helper */ + element++; + + return element; +} + +/** + * Free memory previously allocated by mem_malloc. Loads the pool number + * and calls memp_free with that pool number to put the element back into + * its pool + * + * @param rmem the memory element to free + */ +void +mem_free(void *rmem) +{ + struct memp_malloc_helper *hmem = (struct memp_malloc_helper*)rmem; + + LWIP_ASSERT("rmem != NULL", (rmem != NULL)); + LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); + + /* get the original struct memp_malloc_helper */ + hmem--; + + LWIP_ASSERT("hmem != NULL", (hmem != NULL)); + LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); + LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX)); + + /* and put it in the pool we saved earlier */ + memp_free(hmem->poolnr, hmem); +} + +#else /* MEM_USE_POOLS */ +/* lwIP replacement for your libc malloc() */ + +/** + * The heap is made up as a list of structs of this type. + * This does not have to be aligned since for getting its size, + * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes. + */ +struct mem { + /** index (-> ram[next]) of the next struct */ + mem_size_t next; + /** index (-> ram[prev]) of the previous struct */ + mem_size_t prev; + /** 1: this area is used; 0: this area is unused */ + u8_t used; + u8_t pad[3]; /* XXX: pad here instead use global ALIGN */ +} __ATTRIB_PACK; + +/** All allocated blocks will be MIN_SIZE bytes big, at least! + * MIN_SIZE can be overridden to suit your needs. Smaller values save space, + * larger values could prevent too small blocks to fragment the RAM too much. */ +#ifndef MIN_SIZE +#define MIN_SIZE 12 +#endif /* MIN_SIZE */ +/* some alignment macros: we define them here for better source code layout */ +#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE) +#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem)) +#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE) + +/** If you want to relocate the heap to external memory, simply define + * LWIP_RAM_HEAP_POINTER as a void-pointer to that location. + * If so, make sure the memory at that location is big enough (see below on + * how that space is calculated). */ +#ifndef LWIP_RAM_HEAP_POINTER +/** the heap. we need one struct mem at the end and some room for alignment */ +/* enlarge heap as tx pbuf payload is allocate from heap as well */ +u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT] SHMEM_ATTR; +#define LWIP_RAM_HEAP_POINTER ram_heap +#endif /* LWIP_RAM_HEAP_POINTER */ + +/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */ +static u8_t *ram; +/** the last entry, always unused! */ +static struct mem *ram_end; +/** pointer to the lowest free block, this is used for faster search */ +static struct mem *lfree; + +/** concurrent access protection */ +//static sys_mutex_t mem_mutex; + +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + +static volatile u8_t mem_free_count; + +/* Allow mem_free from other (e.g. interrupt) context */ +#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free) +#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free) +#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free) +#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc) + +#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + +/* Protect the heap only by using a semaphore */ +#define LWIP_MEM_FREE_DECL_PROTECT() +#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex) +#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex) +/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */ +#define LWIP_MEM_ALLOC_DECL_PROTECT() +#define LWIP_MEM_ALLOC_PROTECT() +#define LWIP_MEM_ALLOC_UNPROTECT() + +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + +/** + * "Plug holes" by combining adjacent empty struct mems. + * After this function is through, there should not exist + * one empty struct mem pointing to another empty struct mem. + * + * @param mem this points to a struct mem which just has been freed + * @internal this function is only called by mem_free() and mem_trim() + * + * This assumes access to the heap is protected by the calling function + * already. + */ +static void ICACHE_FLASH_ATTR +plug_holes(struct mem *mem) +{ + struct mem *nmem; + struct mem *pmem; + + LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram); + LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end); + LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0); + + /* plug hole forward */ + LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED); + + nmem = (struct mem *)(void *)&ram[mem->next]; + if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) { + /* if mem->next is unused and not end of ram, combine mem and mem->next */ + if (lfree == nmem) { + lfree = mem; + } + mem->next = nmem->next; + ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram); + } + + /* plug hole backward */ + pmem = (struct mem *)(void *)&ram[mem->prev]; + if (pmem != mem && pmem->used == 0) { + /* if mem->prev is unused, combine mem and mem->prev */ + if (lfree == mem) { + lfree = pmem; + } + pmem->next = mem->next; + ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram); + } +} + +/** + * Zero the heap and initialize start, end and lowest-free + */ +void +mem_init(void) +{ + struct mem *mem; + + LWIP_ASSERT("Sanity check alignment", + (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0); + + /* align the heap */ + ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER); + /* initialize the start of the heap */ + mem = (struct mem *)(void *)ram; + mem->next = MEM_SIZE_ALIGNED; + mem->prev = 0; + mem->used = 0; + /* initialize the end of the heap */ + ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED]; + ram_end->used = 1; + ram_end->next = MEM_SIZE_ALIGNED; + ram_end->prev = MEM_SIZE_ALIGNED; + + /* initialize the lowest-free pointer to the start of the heap */ + lfree = (struct mem *)(void *)ram; + + MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED); + + if(sys_mutex_new(&mem_mutex) != ERR_OK) { + LWIP_ASSERT("failed to create mem_mutex", 0); + } +} + +/** + * Put a struct mem back on the heap + * + * @param rmem is the data portion of a struct mem as returned by a previous + * call to mem_malloc() + */ +void +mem_free(void *rmem) +{ + struct mem *mem; + LWIP_MEM_FREE_DECL_PROTECT(); + + if (rmem == NULL) { + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n")); + return; + } + LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0); + + LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return; + } + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + /* Get the corresponding struct mem ... */ + mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... which has to be in a used state ... */ + LWIP_ASSERT("mem_free: mem->used", mem->used); + /* ... and is now unused. */ + mem->used = 0; + + if (mem < lfree) { + /* the newly freed struct is now the lowest */ + lfree = mem; + } + + MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram))); + + /* finally, see if prev or next are free also */ + plug_holes(mem); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); +} + +/** + * Shrink memory returned by mem_malloc(). + * + * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked + * @param newsize required size after shrinking (needs to be smaller than or + * equal to the previous size) + * @return for compatibility reasons: is always == rmem, at the moment + * or NULL if newsize is > old size, in which case rmem is NOT touched + * or freed! + */ +void * +mem_trim(void *rmem, mem_size_t newsize) +{ + mem_size_t size; + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; + /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */ + LWIP_MEM_FREE_DECL_PROTECT(); + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + newsize = LWIP_MEM_ALIGN_SIZE(newsize); + + if(newsize < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + newsize = MIN_SIZE_ALIGNED; + } + + if (newsize > MEM_SIZE_ALIGNED) { + return NULL; + } + + LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return rmem; + } + /* Get the corresponding struct mem ... */ + mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... and its offset pointer */ + ptr = (mem_size_t)((u8_t *)mem - ram); + + size = mem->next - ptr - SIZEOF_STRUCT_MEM; + LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size); + if (newsize > size) { + /* not supported */ + return NULL; + } + if (newsize == size) { + /* No change in size, simply return */ + return rmem; + } + + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + + mem2 = (struct mem *)(void *)&ram[mem->next]; + if(mem2->used == 0) { + /* The next struct is unused, we can simply move it at little */ + mem_size_t next; + /* remember the old next pointer */ + next = mem2->next; + /* create new struct mem which is moved directly after the shrinked mem */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + if (lfree == mem2) { + lfree = (struct mem *)(void *)&ram[ptr2]; + } + mem2 = (struct mem *)(void *)&ram[ptr2]; + mem2->used = 0; + /* restore the next pointer */ + mem2->next = next; + /* link it back to mem */ + mem2->prev = ptr; + /* link mem to it */ + mem->next = ptr2; + /* last thing to restore linked list: as we have moved mem2, + * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not + * the end of the heap */ + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_DEC_USED(used, (size - newsize)); + /* no need to plug holes, we've already done that */ + } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) { + /* Next struct is used but there's room for another struct mem with + * at least MIN_SIZE_ALIGNED of data. + * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem + * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED'). + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + mem2 = (struct mem *)(void *)&ram[ptr2]; + if (mem2 < lfree) { + lfree = mem2; + } + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + mem->next = ptr2; + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_DEC_USED(used, (size - newsize)); + /* the original mem->next is used, so no need to plug holes! */ + } + /* else { + next struct mem is used but size between mem and mem2 is not big enough + to create another struct mem + -> don't do anyhting. + -> the remaining space stays unused since it is too small + } */ +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); + return rmem; +} + +/** + * Adam's mem_malloc() plus solution for bug #17922 + * Allocate a block of memory with a minimum of 'size' bytes. + * + * @param size is the minimum size of the requested block in bytes. + * @return pointer to allocated memory or NULL if no free memory was found. + * + * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT). + */ +void * +mem_malloc(mem_size_t size) +{ + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + u8_t local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_ALLOC_DECL_PROTECT(); + + if (size == 0) { + return NULL; + } + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + size = LWIP_MEM_ALIGN_SIZE(size); + + if(size < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + size = MIN_SIZE_ALIGNED; + } + + if (size > MEM_SIZE_ALIGNED) { + return NULL; + } + + /* protect the heap from concurrent access */ + sys_mutex_lock(&mem_mutex); + LWIP_MEM_ALLOC_PROTECT(); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* run as long as a mem_free disturbed mem_malloc */ + do { + local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + /* Scan through the heap searching for a free block that is big enough, + * beginning with the lowest free block. + */ + for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size; + ptr = ((struct mem *)(void *)&ram[ptr])->next) { + mem = (struct mem *)(void *)&ram[ptr]; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* allow mem_free to run */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + local_mem_free_count = mem_free_count; + } + mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + if ((!mem->used) && + (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { + /* mem is not used and at least perfect fit is possible: + * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ + + if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { + /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing + * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') + * -> split large block, create empty remainder, + * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if + * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, + * struct mem would fit in but no data between mem2 and mem2->next + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory + */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + /* create mem2 struct */ + mem2 = (struct mem *)(void *)&ram[ptr2]; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + /* and insert it between mem and mem->next */ + mem->next = ptr2; + mem->used = 1; + + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM)); + } else { + /* (a mem2 struct does no fit into the user data space of mem and mem->next will always + * be used at this point: if not we have 2 unused structs in a row, plug_holes should have + * take care of this). + * -> near fit or excact fit: do not split, no mem2 creation + * also can't move mem->next directly behind mem, since mem->next + * will always be used at this point! + */ + mem->used = 1; + MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram)); + } + + if (mem == lfree) { + /* Find next free block after mem and update lowest free pointer */ + while (lfree->used && lfree != ram_end) { + LWIP_MEM_ALLOC_UNPROTECT(); + /* prevent high interrupt latency... */ + LWIP_MEM_ALLOC_PROTECT(); + lfree = (struct mem *)(void *)&ram[lfree->next]; + } + LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); + } + LWIP_MEM_ALLOC_UNPROTECT(); + sys_mutex_unlock(&mem_mutex); + LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", + (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); + LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", + ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); + LWIP_ASSERT("mem_malloc: sanity check alignment", + (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0); + + return (u8_t *)mem + SIZEOF_STRUCT_MEM; + } + } +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* if we got interrupted by a mem_free, try again */ + } while(local_mem_free_count != 0); +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); + MEM_STATS_INC(err); + LWIP_MEM_ALLOC_UNPROTECT(); + sys_mutex_unlock(&mem_mutex); + return NULL; +} + +#endif /* MEM_USE_POOLS */ +/** + * Contiguously allocates enough space for count objects that are size bytes + * of memory each and returns a pointer to the allocated memory. + * + * The allocated memory is filled with bytes of value zero. + * + * @param count number of objects to allocate + * @param size size of the objects to allocate + * @return pointer to allocated memory / NULL pointer if there is an error + */ +void *mem_calloc(mem_size_t count, mem_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = mem_malloc(count * size); + if (p) { + /* zero the memory */ + memset(p, 0, count * size); + } + return p; +} + +#endif /* !MEM_LIBC_MALLOC */ diff --git a/app/lwip/core/memp.c b/app/lwip/core/memp.c new file mode 100644 index 00000000..16caf20b --- /dev/null +++ b/app/lwip/core/memp.c @@ -0,0 +1,490 @@ +/** + * @file + * Dynamic pool memory manager + * + * lwIP has dedicated pools for many structures (netconn, protocol control blocks, + * packet buffers, ...). All these pools are managed here. + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/udp.h" +#include "lwip/raw.h" +#include "lwip/tcp_impl.h" +#include "lwip/igmp.h" +#include "lwip/api.h" +#include "lwip/api_msg.h" +#include "lwip/tcpip.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/stats.h" +#include "netif/etharp.h" +#include "lwip/ip_frag.h" +#include "lwip/snmp_structs.h" +#include "lwip/snmp_msg.h" +#include "lwip/dns.h" +#include "netif/ppp_oe.h" + +#include + +#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ + +struct memp { + struct memp *next; +#if MEMP_OVERFLOW_CHECK + const char *file; + int line; +#endif /* MEMP_OVERFLOW_CHECK */ +}; + +#if MEMP_OVERFLOW_CHECK +/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning + * and at the end of each element, initialize them as 0xcd and check + * them later. */ +/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free, + * every single element in each pool is checked! + * This is VERY SLOW but also very helpful. */ +/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in + * lwipopts.h to change the amount reserved for checking. */ +#ifndef MEMP_SANITY_REGION_BEFORE +#define MEMP_SANITY_REGION_BEFORE 16 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#if MEMP_SANITY_REGION_BEFORE > 0 +#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE) +#else +#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#ifndef MEMP_SANITY_REGION_AFTER +#define MEMP_SANITY_REGION_AFTER 16 +#endif /* MEMP_SANITY_REGION_AFTER*/ +#if MEMP_SANITY_REGION_AFTER > 0 +#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER) +#else +#define MEMP_SANITY_REGION_AFTER_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_AFTER*/ + +/* MEMP_SIZE: save space for struct memp and for sanity check */ +#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED) +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED) + +#else /* MEMP_OVERFLOW_CHECK */ + +/* No sanity checks + * We don't need to preserve the struct memp while not allocated, so we + * can save a little space and set MEMP_SIZE to 0. + */ +#define MEMP_SIZE 0 +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_OVERFLOW_CHECK */ + +/** This array holds the first free element of each pool. + * Elements form a linked list. */ +static struct memp *memp_tab[MEMP_MAX]; + +#else /* MEMP_MEM_MALLOC */ + +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_MEM_MALLOC */ + +/** This array holds the element sizes of each pool. */ +#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC +static +#endif +const u16_t memp_sizes[MEMP_MAX] = { //LWIP_MEM_ALIGN_SIZE +#define LWIP_MEMPOOL(name,num,size,desc,attr) LWIP_MEM_ALIGN_SIZE(size), +#include "lwip/memp_std.h" +}; + +u16_t memp_sizes_test[1] = {PBUF_POOL_BUFSIZE,}; + +#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ + +/** This array holds the number of elements in each pool. */ +static const u16_t memp_num[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc,attr) (num), +#include "lwip/memp_std.h" +}; + +/** This array holds a textual description of each pool. */ +//#ifdef LWIP_DEBUG +//static const char *memp_desc[MEMP_MAX] = { +const char *memp_desc[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc,attr) (desc), +#include "lwip/memp_std.h" +}; +//#endif /* LWIP_DEBUG */ + +#if MEMP_SEPARATE_POOLS + +/** This creates each memory pool. These are named memp_memory_XXX_base (where + * XXX is the name of the pool defined in memp_std.h). + * To relocate a pool, declare it as extern in cc.h. Example for GCC: + * extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_UDP_PCB_base[]; + */ +#define LWIP_MEMPOOL(name,num,size,desc,attr) u8_t memp_memory_ ## name ## _base \ + [((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))] attr; +#include "lwip/memp_std.h" + +/** This array holds the base of each memory pool. */ +static u8_t *const memp_bases[] = { +#define LWIP_MEMPOOL(name,num,size,desc,attr) memp_memory_ ## name ## _base, +#include "lwip/memp_std.h" +}; + +#else /* MEMP_SEPARATE_POOLS */ + +/** This is the actual memory used by the pools (all pools in one big block). */ +static u8_t memp_memory[MEM_ALIGNMENT - 1 +#define LWIP_MEMPOOL(name,num,size,desc, attr) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) ) +#include "lwip/memp_std.h" +]; + +#endif /* MEMP_SEPARATE_POOLS */ + +#if MEMP_SANITY_CHECK +/** + * Check that memp-lists don't form a circle, modify by ives at 2014.4.23. + */ +static int ICACHE_FLASH_ATTR +memp_sanity(void) +{ + s16_t i; + struct memp *t, *h; + + for (i = 0; i < MEMP_MAX; i++) { + t = memp_tab[i]; + if(t != NULL) { + for (h = t->next; (t != NULL) && (h != NULL); t = t->next, + h = (((h->next != NULL) && (h->next->next != NULL)) ? h->next->next : NULL)) { + if (t == h) { + return 0; + } + } + } + } + return 1; +} +#endif /* MEMP_SANITY_CHECK*/ +#if MEMP_OVERFLOW_CHECK +#if defined(LWIP_DEBUG) && MEMP_STATS +static const char * memp_overflow_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc,attr) "/"desc, +#include "lwip/memp_std.h" + }; +#endif + +/** + * Check if a memp element was victim of an overflow + * (e.g. the restricted area after it has been altered) + * + * @param p the memp element to check + * @param memp_type the pool p comes from + */ +static void ICACHE_FLASH_ATTR +memp_overflow_check_element_overflow(struct memp *p, u16_t memp_type) +{ + u16_t k; + u8_t *m; +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_sizes[memp_type]; + for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) { + if (m[k] != 0xcd) { + char errstr[128] = "detected memp overflow in pool "; + char digit[] = "0"; + if(memp_type >= 10) { + digit[0] = '0' + (memp_type/10); + strcat(errstr, digit); + } + digit[0] = '0' + (memp_type%10); + strcat(errstr, digit); +#if defined(LWIP_DEBUG) && MEMP_STATS + strcat(errstr, memp_overflow_names[memp_type]); +#endif + LWIP_ASSERT(errstr, 0); + } + } +#endif +} + +/** + * Check if a memp element was victim of an underflow + * (e.g. the restricted area before it has been altered) + * + * @param p the memp element to check + * @param memp_type the pool p comes from + */ +static void ICACHE_FLASH_ATTR +memp_overflow_check_element_underflow(struct memp *p, u16_t memp_type) +{ + u16_t k; + u8_t *m; +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) { + if (m[k] != 0xcd) { + char errstr[128] = "detected memp underflow in pool "; + char digit[] = "0"; + if(memp_type >= 10) { + digit[0] = '0' + (memp_type/10); + strcat(errstr, digit); + } + digit[0] = '0' + (memp_type%10); + strcat(errstr, digit); +#if defined(LWIP_DEBUG) && MEMP_STATS + strcat(errstr, memp_overflow_names[memp_type]); +#endif + LWIP_ASSERT(errstr, 0); + } + } +#endif +} + +/** + * Do an overflow check for all elements in every pool. + * + * @see memp_overflow_check_element for a description of the check + */ +static void ICACHE_FLASH_ATTR +memp_overflow_check_all(void) +{ + u16_t i, j; + struct memp *p; + + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); + for (i = 0; i < MEMP_MAX; ++i) { + p = p; + for (j = 0; j < memp_num[i]; ++j) { + memp_overflow_check_element_overflow(p, i); + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); + for (i = 0; i < MEMP_MAX; ++i) { + p = p; + for (j = 0; j < memp_num[i]; ++j) { + memp_overflow_check_element_underflow(p, i); + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +} + +/** + * Initialize the restricted areas of all memp elements in every pool. + */ +static void ICACHE_FLASH_ATTR +memp_overflow_init(void) +{ + u16_t i, j; + struct memp *p; + u8_t *m; + + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); + for (i = 0; i < MEMP_MAX; ++i) { + p = p; + for (j = 0; j < memp_num[i]; ++j) { +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED); +#endif +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_sizes[i]; + memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED); +#endif + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +} +#endif /* MEMP_OVERFLOW_CHECK */ + +/** + * Initialize this module. + * + * Carves out memp_memory into linked lists for each pool-type. + */ +void +memp_init(void) +{ + struct memp *memp; + u16_t i, j; + + for (i = 0; i < MEMP_MAX; ++i) { + MEMP_STATS_AVAIL(used, i, 0); + MEMP_STATS_AVAIL(max, i, 0); + MEMP_STATS_AVAIL(err, i, 0); + MEMP_STATS_AVAIL(avail, i, memp_num[i]); + } + +#if !MEMP_SEPARATE_POOLS + memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + /* for every pool: */ + for (i = 0; i < MEMP_MAX; ++i) { + memp_tab[i] = NULL; +#if MEMP_SEPARATE_POOLS + memp = (struct memp*)memp_bases[i]; +#endif /* MEMP_SEPARATE_POOLS */ + /* create a linked list of memp elements */ + for (j = 0; j < memp_num[i]; ++j) { + memp->next = (struct memp *)memp_tab[i]; + memp_tab[i] = memp; + memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i] +#if MEMP_OVERFLOW_CHECK + + MEMP_SANITY_REGION_AFTER_ALIGNED +#endif + ); + } + } +#if MEMP_OVERFLOW_CHECK + memp_overflow_init(); + /* check everything a first time to see if it worked */ + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK */ +} + +/** + * Get an element from a specific pool. + * + * @param type the pool to get an element from + * + * the debug version has two more parameters: + * @param file file name calling this function + * @param line number of line where this function is called + * + * @return a pointer to the allocated memory or a NULL pointer on error + */ +void * +#if !MEMP_OVERFLOW_CHECK +memp_malloc(memp_t type) +#else +memp_malloc_fn(memp_t type, const char* file, const int line) +#endif +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;); + + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ + + memp = memp_tab[type]; + + if (memp != NULL) { + memp_tab[type] = memp->next; +#if MEMP_OVERFLOW_CHECK + memp->next = NULL; + memp->file = file; + memp->line = line; +#endif /* MEMP_OVERFLOW_CHECK */ + MEMP_STATS_INC_USED(used, type); + LWIP_ASSERT("memp_malloc: memp properly aligned", + ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0); + memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE); + } else { + LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type])); + MEMP_STATS_INC(err, type); + } + + SYS_ARCH_UNPROTECT(old_level); + + return memp; +} + +/** + * Put an element back into its pool. + * + * @param type the pool where to put mem + * @param mem the memp element to free + */ +void +memp_free(memp_t type, void *mem) +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + if (mem == NULL) { + return; + } + LWIP_ASSERT("memp_free: mem properly aligned", + ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0); + + memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE); + + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#else + memp_overflow_check_element_overflow(memp, type); + memp_overflow_check_element_underflow(memp, type); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ +#endif /* MEMP_OVERFLOW_CHECK */ + + MEMP_STATS_DEC(used, type); + + memp->next = memp_tab[type]; + memp_tab[type] = memp; + +#if MEMP_SANITY_CHECK + LWIP_ASSERT("memp sanity", memp_sanity()); +#endif /* MEMP_SANITY_CHECK */ + + SYS_ARCH_UNPROTECT(old_level); +} + +#endif /* MEMP_MEM_MALLOC */ +#if 0 +void memp_dump(void) +{ + printf("sizeof raw_pcb %u, memp_s1 %u, %s\n", sizeof(struct raw_pcb), memp_sizes[0], memp_desc[0]); + printf("sizeof udp_pcb %u, memp_s2 %u, %s\n", sizeof(struct udp_pcb), memp_sizes[1], memp_desc[1]); + printf("sizeof tcp_pcb %u, memp_s3 %u, %s\n", sizeof(struct tcp_pcb), memp_sizes[2], memp_desc[2]); + printf("sizeof tcp_pcb_listen %u, memp_s4 %u, %s\n", sizeof(struct tcp_pcb_listen), memp_sizes[3], memp_desc[3]); + printf("sizeof tcp_seg %u, memp_s5 %u, %s\n", sizeof(struct tcp_seg), memp_sizes[4], memp_desc[4]); + printf("sizeof sys_timeo %u, memp_s6 %u, %s\n", sizeof(struct sys_timeo), memp_sizes[5], memp_desc[5]); + printf("sizeof pbuf %u, memp_s7 %u, %s\n", sizeof(struct pbuf), memp_sizes[6], memp_desc[6]); + printf("align pbuf size %u, memp_s8 %u, %s\n", (PBUF_POOL_BUFSIZE), memp_sizes[7], memp_desc[7]); + printf("TCP_MSS %d PBUF_LINK_HLEN %d ETH_PAD_SIZE %d\n", TCP_MSS, PBUF_LINK_HLEN, ETH_PAD_SIZE); + printf("TCP_MSS + PBUF_LINK_HLEN + ETH_PAD_SIZE %d \n", TCP_MSS+PBUF_LINK_HLEN+ETH_PAD_SIZE+40); + printf("test size %u\n",memp_sizes_test[0]); + printf("sizeof memp_memory_PBUF_pool %u \n", sizeof(memp_memory_PBUF_POOL_base)); +} +#endif //0000 diff --git a/app/lwip/core/netif.c b/app/lwip/core/netif.c new file mode 100644 index 00000000..6a11dcac --- /dev/null +++ b/app/lwip/core/netif.c @@ -0,0 +1,757 @@ +/** + * @file + * lwIP network interface abstraction + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp.h" +#include "lwip/igmp.h" +#include "netif/etharp.h" +#include "lwip/stats.h" +#if ENABLE_LOOPBACK +#include "lwip/sys.h" +#if LWIP_NETIF_LOOPBACK_MULTITHREADING +#include "lwip/tcpip.h" +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_AUTOIP +#include "lwip/autoip.h" +#endif /* LWIP_AUTOIP */ +#if LWIP_DHCP +#include "lwip/dhcp.h" +#endif /* LWIP_DHCP */ + +#if LWIP_NETIF_STATUS_CALLBACK +#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0) +#else +#define NETIF_STATUS_CALLBACK(n) +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0) +#else +#define NETIF_LINK_CALLBACK(n) +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +struct netif *netif_list; +struct netif *netif_default; + +#if LWIP_HAVE_LOOPIF +static struct netif loop_netif; + +/** + * Initialize a lwip network interface structure for a loopback interface + * + * @param netif the lwip network interface structure for this loopif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + */ + static err_t ICACHE_FLASH_ATTR +netif_loopif_init(struct netif *netif) +{ + /* initialize the snmp variables and counters inside the struct netif + * ifSpeed: no assumption can be made! + */ + NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0); + + netif->name[0] = 'l'; + netif->name[1] = 'o'; + netif->output = netif_loop_output; + return ERR_OK; +} +#endif /* LWIP_HAVE_LOOPIF */ + +void +netif_init(void) +{ +#if LWIP_HAVE_LOOPIF + ip_addr_t loop_ipaddr, loop_netmask, loop_gw; + IP4_ADDR(&loop_gw, 127,0,0,1); + IP4_ADDR(&loop_ipaddr, 127,0,0,1); + IP4_ADDR(&loop_netmask, 255,0,0,0); + +#if NO_SYS + netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, ip_input); +#else /* NO_SYS */ + netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, tcpip_input); +#endif /* NO_SYS */ + netif_set_up(&loop_netif); + +#endif /* LWIP_HAVE_LOOPIF */ +} + +/** + * Add a network interface to the list of lwIP netifs. + * + * @param netif a pre-allocated netif structure + * @param ipaddr IP address for the new netif + * @param netmask network mask for the new netif + * @param gw default gateway IP address for the new netif + * @param state opaque data passed to the new netif + * @param init callback function that initializes the interface + * @param input callback function that is called to pass + * ingress packets up in the protocol layer stack. + * + * @return netif, or NULL if failed. + */ +struct netif * +netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input) +{ + static u8_t netifnum = 0; + + LWIP_ASSERT("No init function given", init != NULL); + + /* reset new interface configuration state */ + ip_addr_set_zero(&netif->ip_addr); + ip_addr_set_zero(&netif->netmask); + ip_addr_set_zero(&netif->gw); + netif->flags = 0; +#if LWIP_DHCP + /* netif not under DHCP control by default */ + netif->dhcp = NULL; +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /* netif not under AutoIP control by default */ + netif->autoip = NULL; +#endif /* LWIP_AUTOIP */ +#if LWIP_NETIF_STATUS_CALLBACK + netif->status_callback = NULL; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + netif->link_callback = NULL; +#endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_IGMP + netif->igmp_mac_filter = NULL; +#endif /* LWIP_IGMP */ +#if ENABLE_LOOPBACK + netif->loop_first = NULL; + netif->loop_last = NULL; +#endif /* ENABLE_LOOPBACK */ + + /* remember netif specific state information data */ + netif->state = state; + netif->num = netifnum++; + netif->input = input; +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ +#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS + netif->loop_cnt_current = 0; +#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */ + + netif_set_addr(netif, ipaddr, netmask, gw); + + /* call user specified initialization function for netif */ + if (init(netif) != ERR_OK) { + return NULL; + } + + /* add this netif to the list */ + netif->next = netif_list; + netif_list = netif; + snmp_inc_iflist(); + +#if LWIP_IGMP + /* start IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_start(netif); + } +#endif /* LWIP_IGMP */ + + LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ", + netif->name[0], netif->name[1])); + ip_addr_debug_print(NETIF_DEBUG, ipaddr); + LWIP_DEBUGF(NETIF_DEBUG, (" netmask ")); + ip_addr_debug_print(NETIF_DEBUG, netmask); + LWIP_DEBUGF(NETIF_DEBUG, (" gw ")); + ip_addr_debug_print(NETIF_DEBUG, gw); + LWIP_DEBUGF(NETIF_DEBUG, ("\n")); + return netif; +} + +/** + * Change IP address configuration for a network interface (including netmask + * and default gateway). + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * @param netmask the new netmask + * @param gw the new default gateway + */ +void +netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw) +{ + netif_set_ipaddr(netif, ipaddr); + netif_set_netmask(netif, netmask); + netif_set_gw(netif, gw); +} + +/** + * Remove a network interface from the list of lwIP netifs. + * + * @param netif the network interface to remove + */ +void +netif_remove(struct netif *netif) +{ + if (netif == NULL) { + return; + } + +#if LWIP_IGMP + /* stop IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_stop(netif); + } +#endif /* LWIP_IGMP */ + if (netif_is_up(netif)) { + /* set netif down before removing (call callback function) */ + netif_set_down(netif); + } + + snmp_delete_ipaddridx_tree(netif); + + /* is it the first netif? */ + if (netif_list == netif) { + netif_list = netif->next; + } else { + /* look for netif further down the list */ + struct netif * tmpNetif; + for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) { + if (tmpNetif->next == netif) { + tmpNetif->next = netif->next; + break; + } + } + if (tmpNetif == NULL) + return; /* we didn't find any netif today */ + } + snmp_dec_iflist(); + /* this netif is default? */ + if (netif_default == netif) { + /* reset default netif */ + netif_set_default(NULL); + } + LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") ); +} + +/** + * Find a network interface by searching for its name + * + * @param name the name of the netif (like netif->name) plus concatenated number + * in ascii representation (e.g. 'en0') + */ +struct netif * +netif_find(char *name) +{ + struct netif *netif; + u8_t num; + + if (name == NULL) { + return NULL; + } + + num = name[2] - '0'; + + for(netif = netif_list; netif != NULL; netif = netif->next) { + if (num == netif->num && + name[0] == netif->name[0] && + name[1] == netif->name[1]) { + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1])); + return netif; + } + } + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1])); + return NULL; +} + +/** + * Change the IP address of a network interface + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * + * @note call netif_set_addr() if you also want to change netmask and + * default gateway + */ +void +netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr) +{ + /* TODO: Handling of obsolete pcbs */ + /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */ +#if LWIP_TCP + struct tcp_pcb *pcb; + struct tcp_pcb_listen *lpcb; + + /* address is actually being changed? */ + if (ipaddr && (ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) { + /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */ + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n")); + pcb = tcp_active_pcbs; + while (pcb != NULL) { + /* PCB bound to current local interface address? */ + if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr)) +#if LWIP_AUTOIP + /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */ + && !ip_addr_islinklocal(&(pcb->local_ip)) +#endif /* LWIP_AUTOIP */ + ) { + /* this connection must be aborted */ + struct tcp_pcb *next = pcb->next; + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb)); + tcp_abort(pcb); + pcb = next; + } else { + pcb = pcb->next; + } + } + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + /* PCB bound to current local interface address? */ + if ((!(ip_addr_isany(&(lpcb->local_ip)))) && + (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))) { + /* The PCB is listening to the old ipaddr and + * is set to listen to the new one instead */ + ip_addr_set(&(lpcb->local_ip), ipaddr); + } + } + } +#endif + snmp_delete_ipaddridx_tree(netif); + snmp_delete_iprteidx_tree(0,netif); + /* set new IP address to netif */ + ip_addr_set(&(netif->ip_addr), ipaddr); + snmp_insert_ipaddridx_tree(netif); + snmp_insert_iprteidx_tree(0,netif); + + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->ip_addr), + ip4_addr2_16(&netif->ip_addr), + ip4_addr3_16(&netif->ip_addr), + ip4_addr4_16(&netif->ip_addr))); +} + +/** + * Change the default gateway for a network interface + * + * @param netif the network interface to change + * @param gw the new default gateway + * + * @note call netif_set_addr() if you also want to change ip address and netmask + */ +void +netif_set_gw(struct netif *netif, ip_addr_t *gw) +{ + ip_addr_set(&(netif->gw), gw); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->gw), + ip4_addr2_16(&netif->gw), + ip4_addr3_16(&netif->gw), + ip4_addr4_16(&netif->gw))); +} + +/** + * Change the netmask of a network interface + * + * @param netif the network interface to change + * @param netmask the new netmask + * + * @note call netif_set_addr() if you also want to change ip address and + * default gateway + */ +void +netif_set_netmask(struct netif *netif, ip_addr_t *netmask) +{ + snmp_delete_iprteidx_tree(0, netif); + /* set new netmask to netif */ + ip_addr_set(&(netif->netmask), netmask); + snmp_insert_iprteidx_tree(0, netif); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->netmask), + ip4_addr2_16(&netif->netmask), + ip4_addr3_16(&netif->netmask), + ip4_addr4_16(&netif->netmask))); +} + +/** + * Set a network interface as the default network interface + * (used to output all packets for which no specific route is found) + * + * @param netif the default network interface + */ +void +netif_set_default(struct netif *netif) +{ + if (netif == NULL) { + /* remove default route */ + snmp_delete_iprteidx_tree(1, netif); + } else { + /* install default route */ + snmp_insert_iprteidx_tree(1, netif); + } + netif_default = netif; + LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n", + netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\'')); +} + +/** + * Bring an interface up, available for processing + * traffic. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_up(struct netif *netif) +{ + if (!(netif->flags & NETIF_FLAG_UP)) { + netif->flags |= NETIF_FLAG_UP; + +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif /* LWIP_SNMP */ + + NETIF_STATUS_CALLBACK(netif); + + if (netif->flags & NETIF_FLAG_LINK_UP) { +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & (NETIF_FLAG_ETHARP)) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ + } + } +} + +/** + * Bring an interface down, disabling any traffic processing. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_down(struct netif *netif) +{ + if (netif->flags & NETIF_FLAG_UP) { + netif->flags &= ~NETIF_FLAG_UP; +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif + +#if LWIP_ARP + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_cleanup_netif(netif); + } +#endif /* LWIP_ARP */ + NETIF_STATUS_CALLBACK(netif); + } +} + +#if LWIP_NETIF_STATUS_CALLBACK +/** + * Set callback to be called when interface is brought up/down + */ +void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback) +{ + if (netif) { + netif->status_callback = status_callback; + } +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +/** + * Called by a driver when its link goes up + */ +void netif_set_link_up(struct netif *netif ) +{ + if (!(netif->flags & NETIF_FLAG_LINK_UP)) { + netif->flags |= NETIF_FLAG_LINK_UP; + +#if LWIP_DHCP + if (netif->dhcp) { + dhcp_network_changed(netif); + } +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP + if (netif->autoip) { + autoip_network_changed(netif); + } +#endif /* LWIP_AUTOIP */ + + if (netif->flags & NETIF_FLAG_UP) { +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ + } + NETIF_LINK_CALLBACK(netif); + } +} + +/** + * Called by a driver when its link goes down + */ +void netif_set_link_down(struct netif *netif ) +{ + if (netif->flags & NETIF_FLAG_LINK_UP) { + netif->flags &= ~NETIF_FLAG_LINK_UP; + NETIF_LINK_CALLBACK(netif); + } +} + +#if LWIP_NETIF_LINK_CALLBACK +/** + * Set callback to be called when link is brought up/down + */ +void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback) +{ + if (netif) { + netif->link_callback = link_callback; + } +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if ENABLE_LOOPBACK +/** + * Send an IP packet to be received on the same netif (loopif-like). + * The pbuf is simply copied and handed back to netif->input. + * In multithreaded mode, this is done directly since netif->input must put + * the packet on a queue. + * In callback mode, the packet is put on an internal queue and is fed to + * netif->input by netif_poll(). + * + * @param netif the lwip network interface structure + * @param p the (IP) packet to 'send' + * @param ipaddr the ip address to send the packet to (not used) + * @return ERR_OK if the packet has been sent + * ERR_MEM if the pbuf used to copy the packet couldn't be allocated + */ +err_t +netif_loop_output(struct netif *netif, struct pbuf *p, + ip_addr_t *ipaddr) +{ + struct pbuf *r; + err_t err; + struct pbuf *last; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = 0; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + /* If we have a loopif, SNMP counters are adjusted for it, + * if not they are adjusted for 'netif'. */ +#if LWIP_SNMP +#if LWIP_HAVE_LOOPIF + struct netif *stats_if = &loop_netif; +#else /* LWIP_HAVE_LOOPIF */ + struct netif *stats_if = netif; +#endif /* LWIP_HAVE_LOOPIF */ +#endif /* LWIP_SNMP */ + SYS_ARCH_DECL_PROTECT(lev); + LWIP_UNUSED_ARG(ipaddr); + + /* Allocate a new pbuf */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return ERR_MEM; + } +#if LWIP_LOOPBACK_MAX_PBUFS + clen = pbuf_clen(r); + /* check for overflow or too many pbuf on queue */ + if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) || + ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) { + pbuf_free(r); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return ERR_MEM; + } + netif->loop_cnt_current += clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + + /* Copy the whole pbuf queue p into the single pbuf r */ + if ((err = pbuf_copy(r, p)) != ERR_OK) { + pbuf_free(r); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return err; + } + + /* Put the packet on a linked list which gets emptied through calling + netif_poll(). */ + + /* let last point to the last pbuf in chain r */ + for (last = r; last->next != NULL; last = last->next); + + SYS_ARCH_PROTECT(lev); + if(netif->loop_first != NULL) { + LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL); + netif->loop_last->next = r; + netif->loop_last = last; + } else { + netif->loop_first = r; + netif->loop_last = last; + } + SYS_ARCH_UNPROTECT(lev); + + LINK_STATS_INC(link.xmit); + snmp_add_ifoutoctets(stats_if, p->tot_len); + snmp_inc_ifoutucastpkts(stats_if); + +#if LWIP_NETIF_LOOPBACK_MULTITHREADING + /* For multithreading environment, schedule a call to netif_poll */ + tcpip_callback((tcpip_callback_fn)netif_poll, netif); +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ + + return ERR_OK; +} + +/** + * Call netif_poll() in the main loop of your application. This is to prevent + * reentering non-reentrant functions like tcp_input(). Packets passed to + * netif_loop_output() are put on a list that is passed to netif->input() by + * netif_poll(). + */ +void +netif_poll(struct netif *netif) +{ + struct pbuf *in; + /* If we have a loopif, SNMP counters are adjusted for it, + * if not they are adjusted for 'netif'. */ +#if LWIP_SNMP +#if LWIP_HAVE_LOOPIF + struct netif *stats_if = &loop_netif; +#else /* LWIP_HAVE_LOOPIF */ + struct netif *stats_if = netif; +#endif /* LWIP_HAVE_LOOPIF */ +#endif /* LWIP_SNMP */ + SYS_ARCH_DECL_PROTECT(lev); + + do { + /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */ + SYS_ARCH_PROTECT(lev); + in = netif->loop_first; + if (in != NULL) { + struct pbuf *in_end = in; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = pbuf_clen(in); + /* adjust the number of pbufs on queue */ + LWIP_ASSERT("netif->loop_cnt_current underflow", + ((netif->loop_cnt_current - clen) < netif->loop_cnt_current)); + netif->loop_cnt_current -= clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + while (in_end->len != in_end->tot_len) { + LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL); + in_end = in_end->next; + } + /* 'in_end' now points to the last pbuf from 'in' */ + if (in_end == netif->loop_last) { + /* this was the last pbuf in the list */ + netif->loop_first = netif->loop_last = NULL; + } else { + /* pop the pbuf off the list */ + netif->loop_first = in_end->next; + LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL); + } + /* De-queue the pbuf from its successors on the 'loop_' list. */ + in_end->next = NULL; + } + SYS_ARCH_UNPROTECT(lev); + + if (in != NULL) { + LINK_STATS_INC(link.recv); + snmp_add_ifinoctets(stats_if, in->tot_len); + snmp_inc_ifinucastpkts(stats_if); + /* loopback packets are always IP packets! */ + if (ip_input(in, netif) != ERR_OK) { + pbuf_free(in); + } + /* Don't reference the packet any more! */ + in = NULL; + } + /* go on while there is a packet on the list */ + } while (netif->loop_first != NULL); +} + +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +/** + * Calls netif_poll() for every netif on the netif_list. + */ +void +netif_poll_all(void) +{ + struct netif *netif = netif_list; + /* loop through netifs */ + while (netif != NULL) { + netif_poll(netif); + /* proceed to next network interface */ + netif = netif->next; + } +} +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ diff --git a/app/lwip/core/pbuf.c b/app/lwip/core/pbuf.c new file mode 100644 index 00000000..e30c0c17 --- /dev/null +++ b/app/lwip/core/pbuf.c @@ -0,0 +1,1212 @@ +/** + * @file + * Packet buffer management + * + * Packets are built from the pbuf data structure. It supports dynamic + * memory allocation for packet contents or can reference externally + * managed packet contents both in RAM and ROM. Quick allocation for + * incoming packets is provided through pools with fixed sized pbufs. + * + * A packet may span over multiple pbufs, chained as a singly linked + * list. This is called a "pbuf chain". + * + * Multiple packets may be queued, also using this singly linked list. + * This is called a "packet queue". + * + * So, a packet queue consists of one or more pbuf chains, each of + * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE + * NOT SUPPORTED!!! Use helper structs to queue multiple packets. + * + * The differences between a pbuf chain and a packet queue are very + * precise but subtle. + * + * The last pbuf of a packet has a ->tot_len field that equals the + * ->len field. It can be found by traversing the list. If the last + * pbuf of a packet has a ->next field other than NULL, more packets + * are on the queue. + * + * Therefore, looping through a pbuf of a single packet, has an + * loop end condition (tot_len == p->len), NOT (next == NULL). + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/stats.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "arch/perf.h" +#if TCP_QUEUE_OOSEQ +#include "lwip/tcp_impl.h" +#endif +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +#ifdef EBUF_LWIP +#include "pp/esf_buf.h" +#else +#define EP_OFFSET 0 +#endif /* ESF_LWIP */ + +#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) +/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically + aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */ +#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) + +#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS +#define PBUF_POOL_IS_EMPTY() +#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */ +/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */ +#ifndef PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_FREE_OOSEQ 1 +#endif /* PBUF_POOL_FREE_OOSEQ */ + +#if PBUF_POOL_FREE_OOSEQ +#include "lwip/tcpip.h" +#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() +static u8_t pbuf_free_ooseq_queued; +/** + * Attempt to reclaim some memory from queued out-of-sequence TCP segments + * if we run out of pool pbufs. It's better to give priority to new packets + * if we're running out. + * + * This must be done in the correct thread context therefore this function + * can only be used with NO_SYS=0 and through tcpip_callback. + */ +static void ICACHE_FLASH_ATTR +pbuf_free_ooseq(void* arg) +{ + struct tcp_pcb* pcb; + SYS_ARCH_DECL_PROTECT(old_level); + LWIP_UNUSED_ARG(arg); + + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_queued = 0; + SYS_ARCH_UNPROTECT(old_level); + + for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) { + if (NULL != pcb->ooseq) { + /** Free the ooseq pbufs of one PCB only */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n")); + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + return; + } + } +} + +/** Queue a call to pbuf_free_ooseq if not already queued. */ +static void ICACHE_FLASH_ATTR +pbuf_pool_is_empty(void) +{ + u8_t queued; + SYS_ARCH_DECL_PROTECT(old_level); + + SYS_ARCH_PROTECT(old_level); + queued = pbuf_free_ooseq_queued; + pbuf_free_ooseq_queued = 1; + SYS_ARCH_UNPROTECT(old_level); + + if(!queued) { + /* queue a call to pbuf_free_ooseq if not already queued */ + if(tcpip_callback_with_block(pbuf_free_ooseq, NULL, 0) != ERR_OK) { + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_queued = 0; + SYS_ARCH_UNPROTECT(old_level); + } + } +} +#endif /* PBUF_POOL_FREE_OOSEQ */ +#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */ + +/** + * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). + * + * The actual memory allocated for the pbuf is determined by the + * layer at which the pbuf is allocated and the requested size + * (from the size parameter). + * + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type this parameter decides how and where the pbuf + * should be allocated as follows: + * + * - PBUF_RAM: buffer memory for pbuf is allocated as one large + * chunk. This includes protocol headers as well. + * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for + * protocol headers. Additional headers must be prepended + * by allocating another pbuf and chain in to the front of + * the ROM pbuf. It is assumed that the memory used is really + * similar to ROM in that it is immutable and will not be + * changed. Memory which is dynamic should generally not + * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. + * - PBUF_REF: no buffer memory is allocated for the pbuf, even for + * protocol headers. It is assumed that the pbuf is only + * being used in a single thread. If the pbuf gets queued, + * then pbuf_take should be called to copy the buffer. + * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from + * the pbuf pool that is allocated during pbuf_init(). + * + * @return the allocated pbuf. If multiple pbufs where allocated, this + * is the first pbuf of a pbuf chain. + */ +struct pbuf * +pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) +{ + struct pbuf *p, *q, *r; + u16_t offset; + s32_t rem_len; /* remaining length */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); + + /* determine header offset */ + offset = 0; + switch (layer) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset += PBUF_TRANSPORT_HLEN; + /* FALLTHROUGH */ + case PBUF_IP: + /* add room for IP layer header */ + offset += PBUF_IP_HLEN; + /* FALLTHROUGH */ + case PBUF_LINK: + /* add room for link layer header */ + offset += PBUF_LINK_HLEN; + +#ifdef PBUF_RSV_FOR_WLAN + /* + * 1. LINK_HLEN 14Byte will be remove in WLAN layer + * 2. IEEE80211_HDR_MAX_LEN needs 40 bytes. + * 3. encryption needs exra 4 bytes ahead of actual data payload, and require + * DAddr and SAddr to be 4-byte aligned. + * 4. TRANSPORT and IP are all 20, 4 bytes aligned, nice... + * 5. LCC add 6 bytes more, We don't consider WAPI yet... + * 6. define LWIP_MEM_ALIGN to be 4 Byte aligned, pbuf struct is 16B, Only thing may be + * matter is ether_hdr is not 4B aligned. + * + * So, we need extra (40 + 4 - 14) = 30 and it's happen to be 4-Byte aligned + * + * 1. lwip + * | empty 30B | eth_hdr (14B) | payload ...| + * total: 44B ahead payload + * 2. net80211 + * | max 80211 hdr, 32B | ccmp/tkip iv (8B) | sec rsv(4B) | payload ...| + * total: 40B ahead sec_rsv and 44B ahead payload + * + */ + offset += EP_OFFSET; //remove LINK hdr in wlan +#endif /* PBUF_RSV_FOR_WLAN */ + + break; + case PBUF_RAW: +#ifdef PBUF_RSV_FOR_WLAN + /* + * RAW pbuf suppose + */ + offset += EP_OFFSET; //remove LINK hdr in wlan +#endif /* PBUF_RAW */ + break; + default: + LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); + return NULL; + } + + switch (type) { + case PBUF_POOL: + /* allocate head of pbuf chain into p */ + p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); + if (p == NULL) { + PBUF_POOL_IS_EMPTY(); + return NULL; + } + p->type = type; + p->next = NULL; + + /* make the payload pointer point 'offset' bytes into pbuf data memory */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); + LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + /* the total length of the pbuf chain is the requested size */ + p->tot_len = length; + /* set the length of the first pbuf in the chain */ + p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", + (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); + /* set reference count (needed here in case we fail) */ + p->ref = 1; + + /* now allocate the tail of the pbuf chain */ + + /* remember first pbuf for linkage in next iteration */ + r = p; + /* remaining length to be allocated */ + rem_len = length - p->len; + /* any remaining pbufs to be allocated? */ + while (rem_len > 0) { + q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); + if (q == NULL) { + PBUF_POOL_IS_EMPTY(); + /* free chain so far allocated */ + pbuf_free(p); + /* bail out unsuccesfully */ + return NULL; + } + q->type = type; + q->flags = 0; + q->next = NULL; + /* make previous pbuf point to this pbuf */ + r->next = q; + /* set total length of this pbuf and next in chain */ + LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); + q->tot_len = (u16_t)rem_len; + /* this pbuf length is pool size, unless smaller sized tail */ + q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); + q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); + LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", + ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + q->ref = 1; + /* calculate remaining length to be allocated */ + rem_len -= q->len; + /* remember this pbuf for linkage in next iteration */ + r = q; + } + /* end of chain */ + /*r->next = NULL;*/ + + break; + case PBUF_RAM: + /* If pbuf is to be allocated in RAM, allocate memory for it. */ + p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); + if (p == NULL) { + return NULL; + } + /* Set up internal structure of the pbuf. */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + + LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + break; +#ifdef EBUF_LWIP + case PBUF_ESF_RX: +#endif /* ESF_LWIP */ + /* pbuf references existing (non-volatile static constant) ROM payload? */ + case PBUF_ROM: + /* pbuf references existing (externally allocated) RAM payload? */ + case PBUF_REF: + /* only allocate memory for the pbuf structure */ + p = (struct pbuf *)memp_malloc(MEMP_PBUF); + if (p == NULL) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", + (type == PBUF_ROM) ? "ROM" : "REF")); + return NULL; + } + /* caller must set this field properly, afterwards */ + p->payload = NULL; + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + break; + default: + LWIP_ASSERT("pbuf_alloc: erroneous type", 0); + return NULL; + } + /* set reference count */ + p->ref = 1; + /* set flags */ + p->flags = 0; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); + return p; +} + +#if LWIP_SUPPORT_CUSTOM_PBUF +/** Initialize a custom pbuf (already allocated). + * + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type type of the pbuf (only used to treat the pbuf accordingly, as + * this function allocates no memory) + * @param p pointer to the custom pbuf to initialize (already allocated) + * @param payload_mem pointer to the buffer that is used for payload and headers, + * must be at least big enough to hold 'length' plus the header size, + * may be NULL if set later + * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least + * big enough to hold 'length' plus the header size + */ +struct pbuf* +pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p, + void *payload_mem, u16_t payload_mem_len) +{ + u16_t offset; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length)); + + /* determine header offset */ + offset = 0; + switch (l) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset += PBUF_TRANSPORT_HLEN; + /* FALLTHROUGH */ + case PBUF_IP: + /* add room for IP layer header */ + offset += PBUF_IP_HLEN; + /* FALLTHROUGH */ + case PBUF_LINK: + /* add room for link layer header */ + offset += PBUF_LINK_HLEN; + break; + case PBUF_RAW: + break; + default: + LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0); + return NULL; + } + + if (LWIP_MEM_ALIGN_SIZE(offset) + length < payload_mem_len) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length)); + return NULL; + } + + p->pbuf.next = NULL; + if (payload_mem != NULL) { + p->pbuf.payload = LWIP_MEM_ALIGN((void *)((u8_t *)payload_mem + offset)); + } else { + p->pbuf.payload = NULL; + } + p->pbuf.flags = PBUF_FLAG_IS_CUSTOM; + p->pbuf.len = p->pbuf.tot_len = length; + p->pbuf.type = type; + p->pbuf.ref = 1; + return &p->pbuf; +} +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + +/** + * Shrink a pbuf chain to a desired length. + * + * @param p pbuf to shrink. + * @param new_len desired new length of pbuf chain + * + * Depending on the desired length, the first few pbufs in a chain might + * be skipped and left unchanged. The new last pbuf in the chain will be + * resized, and any remaining pbufs will be freed. + * + * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. + * @note May not be called on a packet queue. + * + * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain). + */ +void +pbuf_realloc(struct pbuf *p, u16_t new_len) +{ + struct pbuf *q; + u16_t rem_len; /* remaining length */ + s32_t grow; + + LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL); + LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL || + p->type == PBUF_ROM || + p->type == PBUF_RAM || + p->type == PBUF_REF); + + /* desired length larger than current length? */ + if (new_len >= p->tot_len) { + /* enlarging not yet supported */ + return; + } + + /* the pbuf chain grows by (new_len - p->tot_len) bytes + * (which may be negative in case of shrinking) */ + grow = new_len - p->tot_len; + + /* first, step over any pbufs that should remain in the chain */ + rem_len = new_len; + q = p; + /* should this pbuf be kept? */ + while (rem_len > q->len) { + /* decrease remaining length by pbuf length */ + rem_len -= q->len; + /* decrease total length indicator */ + LWIP_ASSERT("grow < max_u16_t", grow < 0xffff); + q->tot_len += (u16_t)grow; + /* proceed to next pbuf in chain */ + q = q->next; + LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL); + } + /* we have now reached the new last pbuf (in q) */ + /* rem_len == desired length for pbuf q */ + + /* shrink allocated memory for PBUF_RAM */ + /* (other types merely adjust their length fields */ + if ((q->type == PBUF_RAM) && (rem_len != q->len)) { + /* reallocate and adjust the length of the pbuf that will be split */ + q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len); + LWIP_ASSERT("mem_trim returned q == NULL", q != NULL); + } + /* adjust length fields for new last pbuf */ + q->len = rem_len; + q->tot_len = q->len; + + /* any remaining pbufs in chain? */ + if (q->next != NULL) { + /* free remaining pbufs in chain */ + pbuf_free(q->next); + } + /* q is last packet in chain */ + q->next = NULL; + +} + +/** + * Adjusts the payload pointer to hide or reveal headers in the payload. + * + * Adjusts the ->payload pointer so that space for a header + * (dis)appears in the pbuf payload. + * + * The ->payload, ->tot_len and ->len fields are adjusted. + * + * @param p pbuf to change the header size. + * @param header_size_increment Number of bytes to increment header size which + * increases the size of the pbuf. New space is on the front. + * (Using a negative value decreases the header size.) + * If hdr_size_inc is 0, this function does nothing and returns succesful. + * + * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so + * the call will fail. A check is made that the increase in header size does + * not move the payload pointer in front of the start of the buffer. + * @return non-zero on failure, zero on success. + * + */ +u8_t +pbuf_header(struct pbuf *p, s16_t header_size_increment) +{ + u16_t type; + void *payload; + u16_t increment_magnitude; + + LWIP_ASSERT("p != NULL", p != NULL); + if ((header_size_increment == 0) || (p == NULL)) { + return 0; + } + + if (header_size_increment < 0){ + increment_magnitude = -header_size_increment; + /* Check that we aren't going to move off the end of the pbuf */ + LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;); + } else { + increment_magnitude = header_size_increment; +#if 0 + /* Can't assert these as some callers speculatively call + pbuf_header() to see if it's OK. Will return 1 below instead. */ + /* Check that we've got the correct type of pbuf to work with */ + LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL", + p->type == PBUF_RAM || p->type == PBUF_POOL); + /* Check that we aren't going to move off the beginning of the pbuf */ + LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF", + (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF); +#endif + } + + type = p->type; + /* remember current payload pointer */ + payload = p->payload; + + /* pbuf types containing payloads? */ + if (type == PBUF_RAM || type == PBUF_POOL) { + /* set new payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + /* boundary check fails? */ + if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF + EP_OFFSET) { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_header: failed as %p < %p (not enough space for new header size)\n", + (void *)p->payload, (void *)(p + 1))); + /* restore old payload pointer */ + p->payload = payload; + /* bail out unsuccesfully */ + return 1; + } + /* pbuf types refering to external payloads? */ + } else if (type == PBUF_REF || type == PBUF_ROM) { + /* hide a header in the payload? */ + if ((header_size_increment < 0) && (increment_magnitude <= p->len)) { + /* increase payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + } else { + /* cannot expand payload to front (yet!) + * bail out unsuccesfully */ + if (type == PBUF_REF) { + /* increase payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + } else { + return 1; + } + } + } else { + /* Unknown type */ + LWIP_ASSERT("bad pbuf type", 0); + return 1; + } + /* modify pbuf length fields */ + p->len += header_size_increment; + p->tot_len += header_size_increment; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n", + (void *)payload, (void *)p->payload, header_size_increment)); + + return 0; +} + +/** + * Dereference a pbuf chain or queue and deallocate any no-longer-used + * pbufs at the head of this chain or queue. + * + * Decrements the pbuf reference count. If it reaches zero, the pbuf is + * deallocated. + * + * For a pbuf chain, this is repeated for each pbuf in the chain, + * up to the first pbuf which has a non-zero reference count after + * decrementing. So, when all reference counts are one, the whole + * chain is free'd. + * + * @param p The pbuf (chain) to be dereferenced. + * + * @return the number of pbufs that were de-allocated + * from the head of the chain. + * + * @note MUST NOT be called on a packet queue (Not verified to work yet). + * @note the reference counter of a pbuf equals the number of pointers + * that refer to the pbuf (or into the pbuf). + * + * @internal examples: + * + * Assuming existing chains a->b->c with the following reference + * counts, calling pbuf_free(a) results in: + * + * 1->2->3 becomes ...1->3 + * 3->3->3 becomes 2->3->3 + * 1->1->2 becomes ......1 + * 2->1->1 becomes 1->1->1 + * 1->1->1 becomes ....... + * + */ +u8_t +pbuf_free(struct pbuf *p) +{ + u16_t type; + struct pbuf *q; + u8_t count; + + if (p == NULL) { + LWIP_ASSERT("p != NULL", p != NULL); + /* if assertions are disabled, proceed with debug output */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_free(p == NULL) was called.\n")); + return 0; + } + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p)); + + PERF_START; + + LWIP_ASSERT("pbuf_free: sane type", + p->type == PBUF_RAM || p->type == PBUF_ROM || + p->type == PBUF_REF || p->type == PBUF_POOL +#ifdef EBUF_LWIP + || p->type == PBUF_ESF_RX +#endif //EBUF_LWIP + ); + + count = 0; + /* de-allocate all consecutive pbufs from the head of the chain that + * obtain a zero reference count after decrementing*/ + while (p != NULL) { + u16_t ref; + SYS_ARCH_DECL_PROTECT(old_level); + /* Since decrementing ref cannot be guaranteed to be a single machine operation + * we must protect it. We put the new ref into a local variable to prevent + * further protection. */ + SYS_ARCH_PROTECT(old_level); + /* all pbufs in a chain are referenced at least once */ + LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); + /* decrease reference count (number of pointers to pbuf) */ + ref = --(p->ref); + SYS_ARCH_UNPROTECT(old_level); + /* this pbuf is no longer referenced to? */ + if (ref == 0) { + /* remember next pbuf in chain for next iteration */ + q = p->next; + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p)); + type = p->type; +#if LWIP_SUPPORT_CUSTOM_PBUF + /* is this a custom pbuf? */ + if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) { + struct pbuf_custom *pc = (struct pbuf_custom*)p; + LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL); + pc->custom_free_function(p); + } else +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + { + /* is this a pbuf from the pool? */ + if (type == PBUF_POOL) { + memp_free(MEMP_PBUF_POOL, p); + /* is this a ROM or RAM referencing pbuf? */ + } else if (type == PBUF_ROM || type == PBUF_REF +#ifdef EBUF_LWIP + || type == PBUF_ESF_RX +#endif //EBUF_LWIP + ) { +#ifdef EBUF_LWIP + system_pp_recycle_rx_pkt(p->eb); +#endif //EBUF_LWIP + memp_free(MEMP_PBUF, p); + /* type == PBUF_RAM */ + } else { + mem_free(p); + } + } + count++; + /* proceed to next pbuf */ + p = q; + /* p->ref > 0, this pbuf is still referenced to */ + /* (and so the remaining pbufs in chain as well) */ + } else { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref)); + /* stop walking through the chain */ + p = NULL; + } + } + PERF_STOP("pbuf_free"); + /* return number of de-allocated pbufs */ + return count; +} + +/** + * Count number of pbufs in a chain + * + * @param p first pbuf of chain + * @return the number of pbufs in a chain + */ + +u8_t +pbuf_clen(struct pbuf *p) +{ + u8_t len; + + len = 0; + while (p != NULL) { + ++len; + p = p->next; + } + return len; +} + +/** + * Increment the reference count of the pbuf. + * + * @param p pbuf to increase reference counter of + * + */ +void +pbuf_ref(struct pbuf *p) +{ + SYS_ARCH_DECL_PROTECT(old_level); + /* pbuf given? */ + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + ++(p->ref); + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Concatenate two pbufs (each may be a pbuf chain) and take over + * the caller's reference of the tail pbuf. + * + * @note The caller MAY NOT reference the tail pbuf afterwards. + * Use pbuf_chain() for that purpose. + * + * @see pbuf_chain() + */ + +void +pbuf_cat(struct pbuf *h, struct pbuf *t) +{ + struct pbuf *p; + + LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)", + ((h != NULL) && (t != NULL)), return;); + + /* proceed to last pbuf of chain */ + for (p = h; p->next != NULL; p = p->next) { + /* add total length of second chain to all totals of first chain */ + p->tot_len += t->tot_len; + } + /* { p is last pbuf of first h chain, p->next == NULL } */ + LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len); + LWIP_ASSERT("p->next == NULL", p->next == NULL); + /* add total length of second chain to last pbuf total of first chain */ + p->tot_len += t->tot_len; + /* chain last pbuf of head (p) with first of tail (t) */ + p->next = t; + /* p->next now references t, but the caller will drop its reference to t, + * so netto there is no change to the reference count of t. + */ +} + +/** + * Chain two pbufs (or pbuf chains) together. + * + * The caller MUST call pbuf_free(t) once it has stopped + * using it. Use pbuf_cat() instead if you no longer use t. + * + * @param h head pbuf (chain) + * @param t tail pbuf (chain) + * @note The pbufs MUST belong to the same packet. + * @note MAY NOT be called on a packet queue. + * + * The ->tot_len fields of all pbufs of the head chain are adjusted. + * The ->next field of the last pbuf of the head chain is adjusted. + * The ->ref field of the first pbuf of the tail chain is adjusted. + * + */ +void +pbuf_chain(struct pbuf *h, struct pbuf *t) +{ + pbuf_cat(h, t); + /* t is now referenced by h */ + pbuf_ref(t); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); +} + +/** + * Dechains the first pbuf from its succeeding pbufs in the chain. + * + * Makes p->tot_len field equal to p->len. + * @param p pbuf to dechain + * @return remainder of the pbuf chain, or NULL if it was de-allocated. + * @note May not be called on a packet queue. + */ +struct pbuf * +pbuf_dechain(struct pbuf *p) +{ + struct pbuf *q; + u8_t tail_gone = 1; + /* tail */ + q = p->next; + /* pbuf has successor in chain? */ + if (q != NULL) { + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len); + /* enforce invariant if assertion is disabled */ + q->tot_len = p->tot_len - p->len; + /* decouple pbuf from remainder */ + p->next = NULL; + /* total length of pbuf p is its own length only */ + p->tot_len = p->len; + /* q is no longer referenced by p, free it */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); + tail_gone = pbuf_free(q); + if (tail_gone > 0) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, + ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q)); + } + /* return remaining tail or NULL if deallocated */ + } + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); + return ((tail_gone > 0) ? NULL : q); +} + +/** + * + * Create PBUF_RAM copies of pbufs. + * + * Used to queue packets on behalf of the lwIP stack, such as + * ARP based queueing. + * + * @note You MUST explicitly use p = pbuf_take(p); + * + * @note Only one packet is copied, no packet queue! + * + * @param p_to pbuf destination of the copy + * @param p_from pbuf source of the copy + * + * @return ERR_OK if pbuf was copied + * ERR_ARG if one of the pbufs is NULL or p_to is not big + * enough to hold p_from + */ +err_t +pbuf_copy(struct pbuf *p_to, struct pbuf *p_from) +{ + u16_t offset_to=0, offset_from=0, len; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n", + (void*)p_to, (void*)p_from)); + + /* is the target big enough to hold the source? */ + LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) && + (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;); + + /* iterate through pbuf chain */ + do + { + LWIP_ASSERT("p_to != NULL", p_to != NULL); + /* copy one part of the original chain */ + if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { + /* complete current p_from fits into current p_to */ + len = p_from->len - offset_from; + } else { + /* current p_from does not fit into current p_to */ + len = p_to->len - offset_to; + } + MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len); + offset_to += len; + offset_from += len; + LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len); + if (offset_to == p_to->len) { + /* on to next p_to (if any) */ + offset_to = 0; + p_to = p_to->next; + } + LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len); + if (offset_from >= p_from->len) { + /* on to next p_from (if any) */ + offset_from = 0; + p_from = p_from->next; + } + + if((p_from != NULL) && (p_from->len == p_from->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_from->next == NULL), return ERR_VAL;); + } + if((p_to != NULL) && (p_to->len == p_to->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_to->next == NULL), return ERR_VAL;); + } + } while (p_from); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n")); + return ERR_OK; +} + +/** + * Copy (part of) the contents of a packet buffer + * to an application supplied buffer. + * + * @param buf the pbuf from which to copy data + * @param dataptr the application supplied buffer + * @param len length of data to copy (dataptr must be big enough). No more + * than buf->tot_len will be copied, irrespective of len + * @param offset offset into the packet buffer from where to begin copying len bytes + * @return the number of bytes copied, or 0 on failure + */ +u16_t +pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset) +{ + struct pbuf *p; + u16_t left; + u16_t buf_copy_len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;); + + left = 0; + + if((buf == NULL) || (dataptr == NULL)) { + return 0; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; len != 0 && p != NULL; p = p->next) { + if ((offset != 0) && (offset >= p->len)) { + /* don't copy from this buffer -> on to the next */ + offset -= p->len; + } else { + /* copy from this buffer. maybe only partially. */ + buf_copy_len = p->len - offset; + if (buf_copy_len > len) + buf_copy_len = len; + /* copy the necessary parts of the buffer */ + MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len); + copied_total += buf_copy_len; + left += buf_copy_len; + len -= buf_copy_len; + offset = 0; + } + } + return copied_total; +} + +/** + * Copy application supplied data into a pbuf. + * This function can only be used to copy the equivalent of buf->tot_len data. + * + * @param buf pbuf to fill with data + * @param dataptr application supplied data buffer + * @param len length of the application supplied data buffer + * + * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough + */ +err_t +pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len) +{ + struct pbuf *p; + u16_t buf_copy_len; + u16_t total_copy_len = len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;); + + if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) { + return ERR_ARG; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; total_copy_len != 0; p = p->next) { + LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL); + buf_copy_len = total_copy_len; + if (buf_copy_len > p->len) { + /* this pbuf cannot hold all remaining data */ + buf_copy_len = p->len; + } + /* copy the necessary parts of the buffer */ + MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len); + total_copy_len -= buf_copy_len; + copied_total += buf_copy_len; + } + LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len); + return ERR_OK; +} + +/** + * Creates a single pbuf out of a queue of pbufs. + * + * @remark: Either the source pbuf 'p' is freed by this function or the original + * pbuf 'p' is returned, therefore the caller has to check the result! + * + * @param p the source pbuf + * @param layer pbuf_layer of the new pbuf + * + * @return a new, single pbuf (p->next is NULL) + * or the old pbuf if allocation fails + */ +struct pbuf* +pbuf_coalesce(struct pbuf *p, pbuf_layer layer) +{ + struct pbuf *q; + err_t err; + if (p->next == NULL) { + return p; + } + q = pbuf_alloc(layer, p->tot_len, PBUF_RAM); + if (q == NULL) { + /* @todo: what do we do now? */ + return p; + } + err = pbuf_copy(q, p); + LWIP_ASSERT("pbuf_copy failed", err == ERR_OK); + pbuf_free(p); + return q; +} + +#if LWIP_CHECKSUM_ON_COPY +/** + * Copies data into a single pbuf (*not* into a pbuf queue!) and updates + * the checksum while copying + * + * @param p the pbuf to copy data into + * @param start_offset offset of p->payload where to copy the data to + * @param dataptr data to copy into the pbuf + * @param len length of data to copy into the pbuf + * @param chksum pointer to the checksum which is updated + * @return ERR_OK if successful, another error if the data does not fit + * within the (first) pbuf (no pbuf queues!) + */ +err_t +pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, + u16_t len, u16_t *chksum) +{ + u32_t acc; + u16_t copy_chksum; + char *dst_ptr; + LWIP_ASSERT("p != NULL", p != NULL); + LWIP_ASSERT("dataptr != NULL", dataptr != NULL); + LWIP_ASSERT("chksum != NULL", chksum != NULL); + LWIP_ASSERT("len != 0", len != 0); + + if ((start_offset >= p->len) || (start_offset + len > p->len)) { + return ERR_ARG; + } + + dst_ptr = ((char*)p->payload) + start_offset; + copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len); + if ((start_offset & 1) != 0) { + copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum); + } + acc = *chksum; + acc += copy_chksum; + *chksum = FOLD_U32T(acc); + return ERR_OK; +} +#endif /* LWIP_CHECKSUM_ON_COPY */ + + /** Get one byte from the specified position in a pbuf + * WARNING: returns zero for offset >= p->tot_len + * + * @param p pbuf to parse + * @param offset offset into p of the byte to return + * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len + */ +u8_t +pbuf_get_at(struct pbuf* p, u16_t offset) +{ + u16_t copy_from = offset; + struct pbuf* q = p; + + /* get the correct pbuf */ + while ((q != NULL) && (q->len <= copy_from)) { + copy_from -= q->len; + q = q->next; + } + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->len > copy_from)) { + return ((u8_t*)q->payload)[copy_from]; + } + return 0; +} + +/** Compare pbuf contents at specified offset with memory s2, both of length n + * + * @param p pbuf to compare + * @param offset offset into p at wich to start comparing + * @param s2 buffer to compare + * @param n length of buffer to compare + * @return zero if equal, nonzero otherwise + * (0xffff if p is too short, diffoffset+1 otherwise) + */ +u16_t +pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n) +{ + u16_t start = offset; + struct pbuf* q = p; + + /* get the correct pbuf */ + while ((q != NULL) && (q->len <= start)) { + start -= q->len; + q = q->next; + } + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->len > start)) { + u16_t i; + for(i = 0; i < n; i++) { + u8_t a = pbuf_get_at(q, start + i); + u8_t b = ((u8_t*)s2)[i]; + if (a != b) { + return i+1; + } + } + return 0; + } + return 0xffff; +} + +/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset + * start_offset. + * + * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as + * return value 'not found' + * @param mem search for the contents of this buffer + * @param mem_len length of 'mem' + * @param start_offset offset into p at which to start searching + * @return 0xFFFF if substr was not found in p or the index where it was found + */ +u16_t +pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset) +{ + u16_t i; + u16_t max = p->tot_len - mem_len; + if (p->tot_len >= mem_len + start_offset) { + for(i = start_offset; i <= max; ) { + u16_t plus = pbuf_memcmp(p, i, mem, mem_len); + if (plus == 0) { + return i; + } else { + i += plus; + } + } + } + return 0xFFFF; +} + +/** Find occurrence of substr with length substr_len in pbuf p, start at offset + * start_offset + * WARNING: in contrast to strstr(), this one does not stop at the first \0 in + * the pbuf/source string! + * + * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as + * return value 'not found' + * @param substr string to search for in p, maximum length is 0xFFFE + * @return 0xFFFF if substr was not found in p or the index where it was found + */ +u16_t +pbuf_strstr(struct pbuf* p, const char* substr) +{ + size_t substr_len; + if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) { + return 0xFFFF; + } + substr_len = strlen(substr); + if (substr_len >= 0xFFFF) { + return 0xFFFF; + } + return pbuf_memfind(p, substr, (u16_t)substr_len, 0); +} diff --git a/app/lwip/core/raw.c b/app/lwip/core/raw.c new file mode 100644 index 00000000..d0e65579 --- /dev/null +++ b/app/lwip/core/raw.c @@ -0,0 +1,354 @@ +/** + * @file + * Implementation of raw protocol PCBs for low-level handling of + * different types of protocols besides (or overriding) those + * already available in lwIP. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/raw.h" +#include "lwip/stats.h" +#include "arch/perf.h" + +#include + +/** The list of RAW PCBs */ +static struct raw_pcb *raw_pcbs; + +/** + * Determine if in incoming IP packet is covered by a RAW PCB + * and if so, pass it to a user-provided receive callback function. + * + * Given an incoming IP datagram (as a chain of pbufs) this function + * finds a corresponding RAW PCB and calls the corresponding receive + * callback function. + * + * @param p pbuf to be demultiplexed to a RAW PCB. + * @param inp network interface on which the datagram was received. + * @return - 1 if the packet has been eaten by a RAW PCB receive + * callback function. The caller MAY NOT not reference the + * packet any longer, and MAY NOT call pbuf_free(). + * @return - 0 if packet is not eaten (pbuf is still referenced by the + * caller). + * + */ +u8_t ICACHE_FLASH_ATTR +raw_input(struct pbuf *p, struct netif *inp) +{ + struct raw_pcb *pcb, *prev; + struct ip_hdr *iphdr; + s16_t proto; + u8_t eaten = 0; + + LWIP_UNUSED_ARG(inp); + + iphdr = (struct ip_hdr *)p->payload; + proto = IPH_PROTO(iphdr); + + prev = NULL; + pcb = raw_pcbs; + /* loop through all raw pcbs until the packet is eaten by one */ + /* this allows multiple pcbs to match against the packet by design */ + while ((eaten == 0) && (pcb != NULL)) { + if ((pcb->protocol == proto) && + (ip_addr_isany(&pcb->local_ip) || + ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest))) { +#if IP_SOF_BROADCAST_RECV + /* broadcast filter? */ + if ((pcb->so_options & SOF_BROADCAST) || !ip_addr_isbroadcast(¤t_iphdr_dest, inp)) +#endif /* IP_SOF_BROADCAST_RECV */ + { + /* receive callback function available? */ + if (pcb->recv != NULL) { + /* the receive callback function did not eat the packet? */ + if (pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr()) != 0) { + /* receive function ate the packet */ + p = NULL; + eaten = 1; + if (prev != NULL) { + /* move the pcb to the front of raw_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + } + } + /* no receive callback function was set for this raw PCB */ + } + /* drop the packet */ + } + prev = pcb; + pcb = pcb->next; + } + return eaten; +} + +/** + * Bind a RAW PCB. + * + * @param pcb RAW PCB to be bound with a local address ipaddr. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified IP address is already bound to by + * another RAW PCB. + * + * @see raw_disconnect() + */ +err_t ICACHE_FLASH_ATTR +raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr) +{ + ip_addr_set(&pcb->local_ip, ipaddr); + return ERR_OK; +} + +/** + * Connect an RAW PCB. This function is required by upper layers + * of lwip. Using the raw api you could use raw_sendto() instead + * + * This will associate the RAW PCB with the remote address. + * + * @param pcb RAW PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * + * @return lwIP error code + * + * @see raw_disconnect() and raw_sendto() + */ +err_t ICACHE_FLASH_ATTR +raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr) +{ + ip_addr_set(&pcb->remote_ip, ipaddr); + return ERR_OK; +} + + +/** + * Set the callback function for received packets that match the + * raw PCB's protocol and binding. + * + * The callback function MUST either + * - eat the packet by calling pbuf_free() and returning non-zero. The + * packet will not be passed to other raw PCBs or other protocol layers. + * - not free the packet, and return zero. The packet will be matched + * against further PCBs and/or forwarded to another protocol layers. + * + * @return non-zero if the packet was free()d, zero if the packet remains + * available for others. + */ +void ICACHE_FLASH_ATTR +raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv = recv; + pcb->recv_arg = recv_arg; +} + +/** + * Send the raw IP packet to the given address. Note that actually you cannot + * modify the IP headers (this is inconsistent with the receive callback where + * you actually get the IP headers), you can only specify the IP payload here. + * It requires some more changes in lwIP. (there will be a raw_send() function + * then.) + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * @param ipaddr the destination address of the IP packet + * + */ +err_t ICACHE_FLASH_ATTR +raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) +{ + err_t err; + struct netif *netif; + ip_addr_t *src_ip; + struct pbuf *q; /* q will be sent down the stack */ + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); + + /* not enough space to add an IP header to first pbuf in given p chain? */ + if (pbuf_header(p, IP_HLEN)) { + /* allocate header in new pbuf */ + q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n")); + return ERR_MEM; + } + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p */ + pbuf_chain(q, p); + } + /* { first pbuf q points to header pbuf } */ + LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* first pbuf q equals given pbuf */ + q = p; + if(pbuf_header(q, -IP_HLEN)) { + LWIP_ASSERT("Can't restore header we just removed!", 0); + return ERR_MEM; + } + } + + if ((netif = ip_route(ipaddr)) == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } + +#if IP_SOF_BROADCAST + /* broadcast filter? */ + if (((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(ipaddr, netif)) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_VAL; + } +#endif /* IP_SOF_BROADCAST */ + + if (ip_addr_isany(&pcb->local_ip)) { + /* use outgoing network interface IP address as source address */ + src_ip = &(netif->ip_addr); + } else { + /* use RAW PCB local IP address as source address */ + src_ip = &(pcb->local_ip); + } + +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = &(pcb->addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT*/ + err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ + + /* did we chain a header earlier? */ + if (q != p) { + /* free the header */ + pbuf_free(q); + } + return err; +} + +/** + * Send the raw IP packet to the address given by raw_connect() + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * + */ +err_t ICACHE_FLASH_ATTR +raw_send(struct raw_pcb *pcb, struct pbuf *p) +{ + return raw_sendto(pcb, p, &pcb->remote_ip); +} + +/** + * Remove an RAW PCB. + * + * @param pcb RAW PCB to be removed. The PCB is removed from the list of + * RAW PCB's and the data structure is freed from memory. + * + * @see raw_new() + */ +void ICACHE_FLASH_ATTR +raw_remove(struct raw_pcb *pcb) +{ + struct raw_pcb *pcb2; + /* pcb to be removed is first in list? */ + if (raw_pcbs == pcb) { + /* make list start at 2nd pcb */ + raw_pcbs = raw_pcbs->next; + /* pcb not 1st in list */ + } else { + for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in raw_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + } + memp_free(MEMP_RAW_PCB, pcb); +} + +/** + * Create a RAW PCB. + * + * @return The RAW PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP) + * + * @see raw_remove() + */ +struct raw_pcb * ICACHE_FLASH_ATTR +raw_new(u8_t proto) +{ + struct raw_pcb *pcb; + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n")); + + pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB); + /* could allocate RAW PCB? */ + if (pcb != NULL) { + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct raw_pcb)); + pcb->protocol = proto; + pcb->ttl = RAW_TTL; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + return pcb; +} + +#endif /* LWIP_RAW */ diff --git a/app/lwip/core/stats.c b/app/lwip/core/stats.c new file mode 100644 index 00000000..69f97d41 --- /dev/null +++ b/app/lwip/core/stats.c @@ -0,0 +1,176 @@ +/** + * @file + * Statistics module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/mem.h" + +#include + +struct stats_ lwip_stats; + +void stats_init(void) +{ +#ifdef LWIP_DEBUG +#if MEMP_STATS + const char * memp_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) desc, +#include "lwip/memp_std.h" + }; + int i; + for (i = 0; i < MEMP_MAX; i++) { + lwip_stats.memp[i].name = memp_names[i]; + } +#endif /* MEMP_STATS */ +#if MEM_STATS + lwip_stats.mem.name = "MEM"; +#endif /* MEM_STATS */ +#endif /* LWIP_DEBUG */ +} + +#if LWIP_STATS_DISPLAY +void +stats_display_proto(struct stats_proto *proto, char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv)); + LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr)); + LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr)); + LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr)); + LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err)); + LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit)); +} + +#if IGMP_STATS +void +stats_display_igmp(struct stats_igmp *igmp) +{ + LWIP_PLATFORM_DIAG(("\nIGMP\n\t")); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr)); + LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1)); + LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n", igmp->rx_group)); + LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n", igmp->rx_general)); + LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report)); + LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join)); + LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave)); + LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n\t", igmp->tx_report)); +} +#endif /* IGMP_STATS */ + +#if MEM_STATS || MEMP_STATS +void +stats_display_mem(struct stats_mem *mem, char *name) +{ + LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name)); + LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail)); + LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used)); + LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max)); + LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err)); +} + +#if MEMP_STATS +void +stats_display_memp(struct stats_mem *mem, int index) +{ + char * memp_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) desc, +#include "lwip/memp_std.h" + }; + if(index < MEMP_MAX) { + stats_display_mem(mem, memp_names[index]); + } +} +#endif /* MEMP_STATS */ +#endif /* MEM_STATS || MEMP_STATS */ + +#if SYS_STATS +void +stats_display_sys(struct stats_sys *sys) +{ + LWIP_PLATFORM_DIAG(("\nSYS\n\t")); + LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used)); + LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max)); + LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err)); + LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used)); + LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max)); + LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err)); + LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used)); + LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max)); + LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err)); +} +#endif /* SYS_STATS */ + +void +stats_display(void) +{ + s16_t i; + + LINK_STATS_DISPLAY(); + ETHARP_STATS_DISPLAY(); + IPFRAG_STATS_DISPLAY(); + IP_STATS_DISPLAY(); + IGMP_STATS_DISPLAY(); + ICMP_STATS_DISPLAY(); + UDP_STATS_DISPLAY(); + TCP_STATS_DISPLAY(); + MEM_STATS_DISPLAY(); + for (i = 0; i < MEMP_MAX; i++) { + MEMP_STATS_DISPLAY(i); + } + SYS_STATS_DISPLAY(); +} +#endif /* LWIP_STATS_DISPLAY */ + +#endif /* LWIP_STATS */ + diff --git a/app/lwip/core/sys.c b/app/lwip/core/sys.c new file mode 100644 index 00000000..d3a77deb --- /dev/null +++ b/app/lwip/core/sys.c @@ -0,0 +1,66 @@ +/** + * @file + * lwIP Operating System abstraction + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/sys.h" + +/* Most of the functions defined in sys.h must be implemented in the + * architecture-dependent file sys_arch.c */ + +#if !NO_SYS + +/** + * Sleep for some ms. Timeouts are NOT processed while sleeping. + * + * @param ms number of milliseconds to sleep + */ +void +sys_msleep(u32_t ms) +{ + if (ms > 0) { + sys_sem_t delaysem; + err_t err = sys_sem_new(&delaysem, 0); + if (err == ERR_OK) { + sys_arch_sem_wait(&delaysem, ms); + sys_sem_free(&delaysem); + } + } +} + +#endif /* !NO_SYS */ diff --git a/app/lwip/core/sys_arch.c b/app/lwip/core/sys_arch.c new file mode 100644 index 00000000..81bb2208 --- /dev/null +++ b/app/lwip/core/sys_arch.c @@ -0,0 +1,13 @@ +/* + * copyright (c) 2010 - 2011 espressif system + */ + +#include "c_types.h" +#include "ets_sys.h" +#include "osapi.h" +#include "os_type.h" + +#include "lwip/opt.h" +#include "lwip/sys.h" + +#include "eagle_soc.h" diff --git a/app/lwip/core/tcp.c b/app/lwip/core/tcp.c new file mode 100644 index 00000000..e3193cc7 --- /dev/null +++ b/app/lwip/core/tcp.c @@ -0,0 +1,1651 @@ +/** + * @file + * Transmission Control Protocol for IP + * + * This file contains common functions for the TCP implementation, such as functinos + * for manipulating the data structures and the TCP timer functions. TCP functions + * related to input and output is found in tcp_in.c and tcp_out.c respectively. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/snmp.h" +#include "lwip/tcp.h" +#include "lwip/tcp_impl.h" +#include "lwip/debug.h" +#include "lwip/stats.h" + +#include + +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" +}; + +/* Incremented every coarse grained timer shot (typically every 500 ms). */ +u32_t tcp_ticks; +const u8_t tcp_backoff[13] = + { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; + /* Times per slowtmr hits */ +const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; + +/* The TCP PCB lists. */ + +/** List of all TCP PCBs bound but not yet (connected || listening) */ +struct tcp_pcb *tcp_bound_pcbs; +/** List of all TCP PCBs in LISTEN state */ +union tcp_listen_pcbs_t tcp_listen_pcbs; +/** List of all TCP PCBs that are in a state in which + * they accept or send data. */ +struct tcp_pcb *tcp_active_pcbs; +/** List of all TCP PCBs in TIME-WAIT state */ +struct tcp_pcb *tcp_tw_pcbs; + +#define NUM_TCP_PCB_LISTS 4 +#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3 +/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */ +struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, + &tcp_active_pcbs, &tcp_tw_pcbs}; + +/** Only used for temporary storage. */ +struct tcp_pcb *tcp_tmp_pcb; + +/** Timer counter to handle calling slow-timer from tcp_tmr() */ +static u8_t tcp_timer; +static u16_t tcp_new_port(void);//����µ�tcp���ض˿� + +/** + * Called periodically to dispatch TCP timers. + * + */ +void +tcp_tmr(void) +{ + /* Call tcp_fasttmr() every 250 ms */ + tcp_fasttmr(); + + if (++tcp_timer & 1) { + /* Call tcp_tmr() every 500 ms, i.e., every other timer + tcp_tmr() is called. */ + tcp_slowtmr(); + } +} + +/** + * Closes the TX side of a connection held by the PCB. + * For tcp_close(), a RST is sent if the application didn't receive all data + * (tcp_recved() not called for all data passed to recv callback). + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it. + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ +static err_t +tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) +{ + err_t err; + + if (rst_on_unacked_data && (pcb->state != LISTEN)) { + if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) { + /* Not all data received by application, send RST to tell the remote + side about this. */ + LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED); + + /* don't call tcp_abort here: we must not deallocate the pcb since + that might not be expected when calling tcp_close */ + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port); + + tcp_pcb_purge(pcb); + + /* TODO: to which state do we move now? */ + + /* move to TIME_WAIT since we close actively */ + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + + return ERR_OK; + } + } + + switch (pcb->state) { + case CLOSED: + /* Closing a pcb in the CLOSED state might seem erroneous, + * however, it is in this state once allocated and as yet unused + * and the user needs some way to free it should the need arise. + * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) + * or for a pcb that has been used and then entered the CLOSED state + * is erroneous, but this should never happen as the pcb has in those cases + * been freed, and so any remaining handles are bogus. */ + /*��CLOSED״̬�¹ر�һ��pcb�ƺ��Ǵ���ģ� + *������ˣ�һ�������״̬�·����˶��һ�û��ʹ��,�û���ҪһЩ�취���ͷ��� + *����һ���Ѿ����رյ�pcb��tcp_close(),(��2��)����һ���Ѿ���ʹ����֮�󣬽���CLOSE״̬�Ǵ���� + *������Щ����±��ͷŵ�pcb�Dz�����ڵ�,��ˣ��κ�ʣ��ľ���Ǽٵ� + */ + err = ERR_OK;//�趨����ֵ + if (pcb->local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb);//��MEMP_TCP_PCB�ڴ���趨�ͷŵ���pcb��Ӧ�ĵ�Ԫֵ,�ͷ��ڴ� + pcb = NULL; + break; + case LISTEN: + err = ERR_OK; + tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);//�Ӽ����PCB�б���ɾ���Ӧ��pcb + memp_free(MEMP_TCP_PCB_LISTEN, pcb);//��MEMP_TCP_PCB_LISTEN�ڴ�����趨�ͷŵ�pcb��Ԫֵ ,�ͷ��ڴ� + pcb = NULL; + break; + case SYN_SENT: + err = ERR_OK; + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + snmp_inc_tcpattemptfails(); + break; + case SYN_RCVD: + err = tcp_send_fin(pcb);//���������ر�FIN���ֱ��� + if (err == ERR_OK) { + snmp_inc_tcpattemptfails(); + pcb->state = FIN_WAIT_1;//ת��FIN_WAIT_1״̬ + } + break; + case ESTABLISHED: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = FIN_WAIT_1; + } + break; + case CLOSE_WAIT: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = LAST_ACK;//����LAST_ACK�ȴ�ACK��ʱ + } + break; + default: + /* Has already been closed, do nothing. */ + err = ERR_OK; + pcb = NULL; + break; + } + + if (pcb != NULL && err == ERR_OK) { + /* To ensure all data has been sent when tcp_close returns, we have + to make sure tcp_output doesn't fail. + Since we don't really have to ensure all data has been sent when tcp_close + returns (unsent data is sent from tcp timer functions, also), we don't care + for the return value of tcp_output for now. */ + /* @todo: When implementing SO_LINGER, this must be changed somehow: + If SOF_LINGER is set, the data should be sent and acked before close returns. + This can only be valid for sequential APIs, not for the raw API. */ + tcp_output(pcb);//���ú����Ϳ��ƿ������ʣ��ı��ģ�����FIN���ֱ��Ķ� + } + return err; +} + +/** + * Closes the connection held by the PCB. + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it (unless an error is returned). + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ + /* + *ͨ��PCB�ر��������� + *�����е�pcbӦ�ñ��ͷŵģ�Ҳ����ԶҲ���ᱻʹ���� + *���û�����ӻ�����Ҳû�б�����,���ӵ�pcbӦ�ñ��ͷŵ� + *���һ�����ӱ�����(����SYN�Ѿ������ջ�����һ���ر��е�״̬) + *���ӱ��ر��ˣ�����������һ�����ڹرյ�״̬ + *pcb�Զ���tcp_slowtmr()�ͷ�,�����������Dz���ȫ�� + */ +err_t +tcp_close(struct tcp_pcb *pcb) +{ +#if TCP_DEBUG //TCP debug��Ϣ����ӡpcb��״̬ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in ")); + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ + + if (pcb->state != LISTEN) { + /* Set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + } + /* ... and close */ + return tcp_close_shutdown(pcb, 1); +} + +/** + * Causes all or part of a full-duplex connection of this PCB to be shut down. + * This doesn't deallocate the PCB! + * + * @param pcb PCB to shutdown + * @param shut_rx shut down receive side if this is != 0 + * @param shut_tx shut down send side if this is != 0 + * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down) + * another err_t on error. + */ +err_t +tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx) +{ + if (pcb->state == LISTEN) { + return ERR_CONN; + } + if (shut_rx) { + /* shut down the receive side: free buffered data... */ + if (pcb->refused_data != NULL) { + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + /* ... and set a flag not to receive any more data */ + pcb->flags |= TF_RXCLOSED; + } + if (shut_tx) { + /* This can't happen twice since if it succeeds, the pcb's state is changed. + Only close in these states as the others directly deallocate the PCB */ + switch (pcb->state) { + case SYN_RCVD: + case ESTABLISHED: + case CLOSE_WAIT: + return tcp_close_shutdown(pcb, 0); + default: + /* don't shut down other states */ + break; + } + } + /* @todo: return another err_t if not in correct state or already shut? */ + return ERR_OK; +} + +/** + * Abandons a connection and optionally sends a RST to the remote + * host. Deletes the local protocol control block. This is done when + * a connection is killed because of shortage of memory. + * + * @param pcb the tcp_pcb to abort + * @param reset boolean to indicate whether a reset should be sent + */ +void +tcp_abandon(struct tcp_pcb *pcb, int reset) +{ + u32_t seqno, ackno; + u16_t remote_port, local_port; + ip_addr_t remote_ip, local_ip; +#if LWIP_CALLBACK_API + tcp_err_fn errf; +#endif /* LWIP_CALLBACK_API */ + void *errf_arg; + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs", + pcb->state != LISTEN); + /* Figure out on which TCP PCB list we are, and remove us. If we + are in an active state, call the receive function associated with + the PCB with a NULL argument, and send an RST to the remote end. */ + if (pcb->state == TIME_WAIT) { + tcp_pcb_remove(&tcp_tw_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + seqno = pcb->snd_nxt; + ackno = pcb->rcv_nxt; + ip_addr_copy(local_ip, pcb->local_ip); + ip_addr_copy(remote_ip, pcb->remote_ip); + local_port = pcb->local_port; + remote_port = pcb->remote_port; +#if LWIP_CALLBACK_API + errf = pcb->errf; +#endif /* LWIP_CALLBACK_API */ + errf_arg = pcb->callback_arg; + tcp_pcb_remove(&tcp_active_pcbs, pcb); + if (pcb->unacked != NULL) { + tcp_segs_free(pcb->unacked); + } + if (pcb->unsent != NULL) { + tcp_segs_free(pcb->unsent); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + tcp_segs_free(pcb->ooseq); + } +#endif /* TCP_QUEUE_OOSEQ */ + if (reset) { + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n")); + tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port); + } + TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); + memp_free(MEMP_TCP_PCB, pcb); + } +} + +/** + * Aborts the connection by sending a RST (reset) segment to the remote + * host. The pcb is deallocated. This function never fails. + * + * ATTENTION: When calling this from one of the TCP callbacks, make + * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise + * or you will risk accessing deallocated memory or memory leaks! + * + * @param pcb the tcp pcb to abort + */ +void +tcp_abort(struct tcp_pcb *pcb) +{ + tcp_abandon(pcb, 1); +} + +/** + * Binds the connection to a local portnumber and IP address. If the + * IP address is not given (i.e., ipaddr == NULL), the IP address of + * the outgoing network interface is used instead. + * + * @param pcb the tcp_pcb to bind (no check is done whether this pcb is + * already bound!) + * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind + * to any local address + * @param port the local port to bind to + * @return ERR_USE if the port is already in use + * ERR_OK if bound + */ +err_t +tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + int i; + int max_pcb_list = NUM_TCP_PCB_LISTS; + struct tcp_pcb *cpcb; + + LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + +#if SO_REUSE + /* Unless the REUSEADDR flag is set, + we have to check the pcbs in TIME-WAIT state, also. + We do not dump TIME_WAIT pcb's; they can still be matched by incoming + packets using both local and remote IP addresses and ports to distinguish. + */ + if ((pcb->so_options & SOF_REUSEADDR) != 0) { + max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT; + } +#endif /* SO_REUSE */ + + if (port == 0) { + port = tcp_new_port(); + } + + /* Check if the address already is in use (on all lists) */ + for (i = 0; i < max_pcb_list; i++) { + for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { +#if SO_REUSE + /* Omit checking for the same port if both pcbs have REUSEADDR set. + For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in + tcp_connect. */ + if (((pcb->so_options & SOF_REUSEADDR) == 0) || + ((cpcb->so_options & SOF_REUSEADDR) == 0)) +#endif /* SO_REUSE */ + { + if (ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + //os_printf("Address in use\n"); + return ERR_USE; + } + } + } + } + } + + if (!ip_addr_isany(ipaddr)) { + pcb->local_ip = *ipaddr; + } + pcb->local_port = port; + TCP_REG(&tcp_bound_pcbs, pcb); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port)); + return ERR_OK; +} +#if LWIP_CALLBACK_API +/** + * Default accept callback if no accept callback is specified by the user. + */ +static err_t +tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(err); + + return ERR_ABRT; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Set the state of the connection to be LISTEN, which means that it + * is able to accept incoming connections. The protocol control block + * is reallocated in order to consume less memory. Setting the + * connection to LISTEN is an irreversible process. + *��ij���󶨵Ŀ��ƿ���Ϊ����״̬ + * @param pcb the original tcp_pcb �����Ŀ��ƿ���� + * @param backlog the incoming connections queue limit + * @return tcp_pcb used for listening, consumes less memory.ָ������״̬�Ŀ��ƿ� + * + * @note The original tcp_pcb is freed. This function therefore has to be + * called like this: + * tpcb = tcp_listen(tpcb); + */ +struct tcp_pcb * +tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) +{ + struct tcp_pcb_listen *lpcb; + + LWIP_UNUSED_ARG(backlog); + LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL); + + /* already listening? */ + if (pcb->state == LISTEN) { + return pcb; + } +#if SO_REUSE + if ((pcb->so_options & SOF_REUSEADDR) != 0) { + /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage + is declared (listen-/connection-pcb), we have to make sure now that + this port is only used once for every local IP. */ + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if (lpcb->local_port == pcb->local_port) { + if (ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) { + /* this address/port is already used */ + return NULL; + } + } + } + } +#endif /* SO_REUSE */ + lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);//�����ڴ�ؿռ� + if (lpcb == NULL) { + return NULL; + } + lpcb->callback_arg = pcb->callback_arg; + lpcb->local_port = pcb->local_port; + lpcb->state = LISTEN; + lpcb->prio = pcb->prio; + lpcb->so_options = pcb->so_options; + lpcb->so_options |= SOF_ACCEPTCONN; + lpcb->ttl = pcb->ttl; + lpcb->tos = pcb->tos; + ip_addr_copy(lpcb->local_ip, pcb->local_ip); + if (pcb->local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); +#if LWIP_CALLBACK_API + lpcb->accept = tcp_accept_null;//���ܿͻ������ӵ�Ĭ�ϻص����� +#endif /* LWIP_CALLBACK_API */ +#if TCP_LISTEN_BACKLOG + lpcb->accepts_pending = 0; + lpcb->backlog = (backlog ? backlog : 1); +#endif /* TCP_LISTEN_BACKLOG */ + TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);//���ƿ����tcp_listen_pcbs�����ײ� + return (struct tcp_pcb *)lpcb; +} + +/** + * Update the state that tracks the available window space to advertise. + * + * Returns how much extra window would be advertised if we sent an + * update now. + */ +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb) +{ + u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd; + + if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) { + /* we can advertise more window */ + pcb->rcv_ann_wnd = pcb->rcv_wnd; + return new_right_edge - pcb->rcv_ann_right_edge; + } else { + if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) { + /* Can happen due to other end sending out of advertised window, + * but within actual available (but not yet advertised) window */ + pcb->rcv_ann_wnd = 0; + } else { + /* keep the right edge of window constant */ + u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt; + LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff); + pcb->rcv_ann_wnd = (u16_t)new_rcv_ann_wnd; + } + return 0; + } +} + +/** + * This function should be called by the application when it has + * processed the data. The purpose is to advertise a larger window + * when the data has been processed. + *Ӧ�ó�����ݴ�����Ϻ�֪ͨ�ں˸��½��մ��� + * @param pcb the tcp_pcb for which data is read + * @param len the amount of bytes that have been read by the application + */ +void +tcp_recved(struct tcp_pcb *pcb, u16_t len) +{ + int wnd_inflation; + + LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n", + len <= 0xffff - pcb->rcv_wnd ); + + pcb->rcv_wnd += len; + if (pcb->rcv_wnd > TCP_WND) { + pcb->rcv_wnd = TCP_WND; + } + + wnd_inflation = tcp_update_rcv_ann_wnd(pcb); + + /* If the change in the right edge of window is significant (default + * watermark is TCP_WND/4), then send an explicit update now. + * Otherwise wait for a packet to be sent in the normal course of + * events (or more window to be available later) */ + if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) { + tcp_ack_now(pcb); + tcp_output(pcb); + } + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n", + len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd)); +} + +/** + * A nastly hack featuring 'goto' statements that allocates a + * new TCP local port. + * + * @return a new (free) local TCP port number + */ +static u16_t +tcp_new_port(void) +{ + int i; + struct tcp_pcb *pcb; +#ifndef TCP_LOCAL_PORT_RANGE_START +#define TCP_LOCAL_PORT_RANGE_START 4096 +#define TCP_LOCAL_PORT_RANGE_END 0x7fff +#endif + static u16_t port = TCP_LOCAL_PORT_RANGE_START; + + again: + if (++port >= TCP_LOCAL_PORT_RANGE_END) { + port = TCP_LOCAL_PORT_RANGE_START; + } + /* Check all PCB lists. */ + for (i = 0; i < NUM_TCP_PCB_LISTS; i++) { + for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == port) { + goto again; + } + } + } + return port; +} + +/** + * Connects to another host. The function given as the "connected" + * argument will be called when the connection has been established. + *�����������һ��SYN���ֱ��� + * @param pcb the tcp_pcb used to establish the connection �����Ŀ��ƿ���� + * @param ipaddr the remote ip address to connect to ������IP��ַ + * @param port the remote tcp port to connect to �������˿ں� + * @param connected callback function to call when connected (or on error) + * @return ERR_VAL if invalid arguments are given + * ERR_OK if connect request has been sent + * other err_t values if connect request couldn't be sent + */ +err_t +tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, + tcp_connected_fn connected) +{ + err_t ret; + u32_t iss; + u16_t old_local_port; + + LWIP_ERROR("tcp_connect: can only connected from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); + if (ipaddr != NULL) { + pcb->remote_ip = *ipaddr;//������IP��ַ��Ч�������Ӽ�¼�м�¼��IP��ַ�����򷵻ش��� + } else { + return ERR_VAL; + } + pcb->remote_port = port;//��¼�������˿�(Ŀ�Ķ˿�) + + /* check if we have a route to the remote host */ + if (ip_addr_isany(&(pcb->local_ip))) { + /* no local IP address set, yet. */ + struct netif *netif = ip_route(&(pcb->remote_ip)); + if (netif == NULL) { + /* Don't even try to send a SYN packet if we have no route + since that will fail. */ + return ERR_RTE; + } + /* Use the netif's IP address as local address. */ + ip_addr_copy(pcb->local_ip, netif->ip_addr); + } + + old_local_port = pcb->local_port; + if (pcb->local_port == 0) { + pcb->local_port = tcp_new_port(); + + } +#if SO_REUSE + if ((pcb->so_options & SOF_REUSEADDR) != 0) { + /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure + now that the 5-tuple is unique. */ + struct tcp_pcb *cpcb; + int i; + /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */ + for (i = 2; i < NUM_TCP_PCB_LISTS; i++) { + for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if ((cpcb->local_port == pcb->local_port) && + (cpcb->remote_port == port) && + ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) && + ip_addr_cmp(&cpcb->remote_ip, ipaddr)) { + /* linux returns EISCONN here, but ERR_USE should be OK for us */ + return ERR_USE; + } + } + } + } +#endif /* SO_REUSE */ + iss = tcp_next_iss();//��ʼ����� + pcb->rcv_nxt = 0;//���÷��ʹ��ڵĸ����ֶ� + pcb->snd_nxt = iss; + pcb->lastack = iss - 1; + pcb->snd_lbb = iss - 1; + pcb->rcv_wnd = TCP_WND;//����Ĭ�Ͻ��մ��ڸ����ֶ�ֵ + pcb->rcv_ann_wnd = TCP_WND; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; + pcb->snd_wnd = TCP_WND; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;//��ʼ������Ķδ�С +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + pcb->cwnd = 1;//��ʼ������� + pcb->ssthresh = pcb->mss * 10; +#if LWIP_CALLBACK_API + pcb->connected = connected;//ע��connected�ص����� +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(connected); +#endif /* LWIP_CALLBACK_API */ + + /* Send a SYN together with the MSS option. */ + ret = tcp_enqueue_flags(pcb, TCP_SYN); + if (ret == ERR_OK) { + /* SYN segment was enqueued, changed the pcbs state now ���ƿ�����ΪSYN_SENT ״̬*/ + pcb->state = SYN_SENT; + if (old_local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + TCP_REG(&tcp_active_pcbs, pcb); + snmp_inc_tcpactiveopens(); + + tcp_output(pcb);//�����ƿ������ӵı��ķ��ͳ�ȥ + } + return ret; +} + +/** + * Called every 500 ms and implements the retransmission timer and the timer that + * removes PCBs that have been in TIME-WAIT for enough time. It also increments + * various timers such as the inactivity timer in each PCB. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_slowtmr(void) +{ + struct tcp_pcb *pcb, *prev; + u16_t eff_wnd; + u8_t pcb_remove; /* flag if a PCB should be removed */ + u8_t pcb_reset; /* flag if a RST should be sent when removing */ + err_t err; + + err = ERR_OK; + + ++tcp_ticks; + + /* Steps through all of the active PCBs. */ + prev = NULL; + pcb = tcp_active_pcbs; + if (pcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n")); + } + while (pcb != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n")); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT); + + pcb_remove = 0; + pcb_reset = 0; + + if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n")); + } + else if (pcb->nrtx == TCP_MAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n")); + } else { + if (pcb->persist_backoff > 0) { + /* If snd_wnd is zero, use persist timer to send 1 byte probes + * instead of using the standard retransmission mechanism. */ + pcb->persist_cnt++; + if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) { + pcb->persist_cnt = 0; + if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) { + pcb->persist_backoff++; + } + tcp_zero_window_probe(pcb); + } + } else { + /* Increase the retransmission timer if it is running */ + if(pcb->rtime >= 0) + ++pcb->rtime; + + if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { + /* Time for a retransmission. */ + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F + " pcb->rto %"S16_F"\n", + pcb->rtime, pcb->rto)); + + /* Double retransmission time-out unless we are trying to + * connect to somebody (i.e., we are in SYN_SENT). */ + if (pcb->state != SYN_SENT) { + pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; +// if (pcb->rto >= TCP_MAXRTO) +// pcb->rto >>= 1; + } + + /* Reset the retransmission timer. */ + pcb->rtime = 0; + + /* Reduce congestion window and ssthresh. */ + eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); + pcb->ssthresh = eff_wnd >> 1; + if (pcb->ssthresh < (pcb->mss << 1)) { + pcb->ssthresh = (pcb->mss << 1); + } + pcb->cwnd = pcb->mss; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F + " ssthresh %"U16_F"\n", + pcb->cwnd, pcb->ssthresh)); + + /* The following needs to be called AFTER cwnd is set to one + mss - STJ */ + tcp_rexmit_rto(pcb); + } + } + } + /* Check if this PCB has stayed too long in FIN-WAIT-2 */ + if (pcb->state == FIN_WAIT_2) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n")); + } + } + + /* Check if KEEPALIVE should be sent */ + if((pcb->so_options & SOF_KEEPALIVE) && + ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT))) { +#if LWIP_TCP_KEEPALIVE + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + (pcb->keep_cnt*pcb->keep_intvl)) + / TCP_SLOW_INTERVAL) +#else + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + TCP_MAXIDLE) / TCP_SLOW_INTERVAL) +#endif /* LWIP_TCP_KEEPALIVE */ + { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n", + ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), + ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip))); + + ++pcb_remove; + ++pcb_reset; + } +#if LWIP_TCP_KEEPALIVE + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * pcb->keep_intvl) + / TCP_SLOW_INTERVAL) +#else + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEPINTVL_DEFAULT) + / TCP_SLOW_INTERVAL) +#endif /* LWIP_TCP_KEEPALIVE */ + { + tcp_keepalive(pcb); + pcb->keep_cnt_sent++; + } + } + + /* If this PCB has queued out of sequence data, but has been + inactive for too long, will drop the data (it will eventually + be retransmitted). */ +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL && + (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) { + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n")); + } +#endif /* TCP_QUEUE_OOSEQ */ + + /* Check if this PCB has stayed too long in SYN-RCVD */ + if (pcb->state == SYN_RCVD) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n")); + } + } + + /* Check if this PCB has stayed too long in LAST-ACK */ + if (pcb->state == LAST_ACK) { + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n")); + } + } + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *pcb2; + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_active_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb); + tcp_active_pcbs = pcb->next; + } + + if (pcb_reset) { + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port); + } + + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT); + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + } else { + /* get the 'next' element now and work with 'prev' below (in case of abort) */ + prev = pcb; + pcb = pcb->next; + + /* We check if we should poll the connection. */ + ++prev->polltmr; + if (prev->polltmr >= prev->pollinterval) { + prev->polltmr = 0; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n")); + TCP_EVENT_POLL(prev, err); + /* if err == ERR_ABRT, 'prev' is already deallocated */ + if (err == ERR_OK) { + tcp_output(prev); + } + } + } + } + + + /* Steps through all of the TIME-WAIT PCBs. */ + prev = NULL; + pcb = tcp_tw_pcbs; + while (pcb != NULL) { + LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + pcb_remove = 0; + + /* Check if this PCB has stayed long enough in TIME-WAIT */ + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + } + + + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *pcb2; + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_tw_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb); + tcp_tw_pcbs = pcb->next; + } + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + } else { + prev = pcb; + pcb = pcb->next; + } + } +} + +/** + * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously + * "refused" by upper layer (application) and sends delayed ACKs. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_fasttmr(void) +{ + struct tcp_pcb *pcb = tcp_active_pcbs; + + while(pcb != NULL) { + struct tcp_pcb *next = pcb->next; + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + /* Notify again application with data previously received. */ + err_t err; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_fasttmr: notify kept packet\n")); + TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err); + if (err == ERR_OK) { + pcb->refused_data = NULL; + } else if (err == ERR_ABRT) { + /* if err == ERR_ABRT, 'pcb' is already deallocated */ + pcb = NULL; + } + } + + /* send delayed ACKs */ + if (pcb && (pcb->flags & TF_ACK_DELAY)) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); + tcp_ack_now(pcb); + tcp_output(pcb); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + + pcb = next; + } +} + +/** + * Deallocates a list of TCP segments (tcp_seg structures). + * + * @param seg tcp_seg list of TCP segments to free + */ +void +tcp_segs_free(struct tcp_seg *seg) +{ + while (seg != NULL) { + struct tcp_seg *next = seg->next; + tcp_seg_free(seg); + seg = next; + } +} + +/** + * Frees a TCP segment (tcp_seg structure). + * + * @param seg single tcp_seg to free + */ +void +tcp_seg_free(struct tcp_seg *seg) +{ + if (seg != NULL) { + if (seg->p != NULL) { + pbuf_free(seg->p); +#if TCP_DEBUG + seg->p = NULL; +#endif /* TCP_DEBUG */ + } + memp_free(MEMP_TCP_SEG, seg); + } +} + +/** + * Sets the priority of a connection. + * + * @param pcb the tcp_pcb to manipulate + * @param prio new priority + */ +void +tcp_setprio(struct tcp_pcb *pcb, u8_t prio) +{ + pcb->prio = prio; +} + +#if TCP_QUEUE_OOSEQ +/** + * Returns a copy of the given TCP segment. + * The pbuf and data are not copied, only the pointers + * + * @param seg the old tcp_seg + * @return a copy of seg + */ +struct tcp_seg * +tcp_seg_copy(struct tcp_seg *seg) +{ + struct tcp_seg *cseg; + + cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG); + if (cseg == NULL) { + return NULL; + } + SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg)); + pbuf_ref(cseg->p); + return cseg; +} +#endif /* TCP_QUEUE_OOSEQ */ + +#if LWIP_CALLBACK_API +/** + * Default receive callback that is called if the user didn't register + * a recv callback for the pcb. + */ +err_t +tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + LWIP_UNUSED_ARG(arg); + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } else if (err == ERR_OK) { + return tcp_close(pcb); + } + return ERR_OK; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Kills the oldest active connection that has lower priority than prio. + * + * @param prio minimum priority + */ +static void ICACHE_FLASH_ATTR +tcp_kill_prio(u8_t prio) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + u8_t mprio; + + + mprio = TCP_PRIO_MAX; + + /* We kill the oldest active connection that has lower priority than prio. */ + inactivity = 0; + inactive = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->prio <= prio && + pcb->prio <= mprio && + (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + mprio = pcb->prio; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Kills the oldest connection that is in TIME_WAIT state. + * Called from tcp_alloc() if no more connections are available. + */ +static void ICACHE_FLASH_ATTR +tcp_kill_timewait(void) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + + inactivity = 0; + inactive = NULL; + /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */ + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Allocate a new tcp_pcb structure. + *����һ��TCP���ƿ�ṹ������ʼ������ֶ� + * @param prio priority for the new pcb �¿��ƿ�����ȼ� + * @return a new tcp_pcb that initially is in state CLOSED ָ���¿��ƿ��ָ�� + */ +struct tcp_pcb * +tcp_alloc(u8_t prio) +{ + struct tcp_pcb *pcb; + u32_t iss; + + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);//�����ڴ�ؿռ� + if (pcb == NULL) { + //os_printf("tcp_pcb memory is fail\n"); + /* Try killing oldest connection in TIME-WAIT. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n")); + tcp_kill_timewait(); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing active connections with lower priority than the new one. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio)); + tcp_kill_prio(prio); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb != NULL) { + /* adjust err stats: memp_malloc failed twice before */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + /* adjust err stats: timewait PCB was freed above */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + memset(pcb, 0, sizeof(struct tcp_pcb)); //��0 + pcb->prio = prio; //�������ȼ� + pcb->snd_buf = TCP_SND_BUF; //��ʹ�õķ��ͻ������С + pcb->snd_queuelen = 0; //��������ռ�õ�pbuf���� + pcb->rcv_wnd = TCP_WND; //���մ��� + pcb->rcv_ann_wnd = TCP_WND; //ͨ����մ��� + pcb->tos = 0; //�������� + pcb->ttl = TCP_TTL; //ttl�ֶ� + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; //��ʼ������Ķ� + pcb->rto = 1000 / TCP_SLOW_INTERVAL; //��ʼ����ʱʱ�� + pcb->sa = 0; //��ʼ����RTT��صIJ��� + pcb->sv = 1000 / TCP_SLOW_INTERVAL; + pcb->rtime = -1; + pcb->cwnd = 1; //��ʼ������� + iss = tcp_next_iss(); //��ó�ʼ���к� + pcb->snd_wl2 = iss; //��ʼ�����ʹ��ڸ����ֶ� + pcb->snd_nxt = iss; + pcb->lastack = iss; + pcb->snd_lbb = iss; + pcb->tmr = tcp_ticks; //��¼���ƿ鴴��ϵͳʱ�� + + pcb->polltmr = 0; //����������¼���ʱ�� + +#if LWIP_CALLBACK_API + pcb->recv = tcp_recv_null; //ע�������ݵ�Ĭ���ϲ㺯�� +#endif /* LWIP_CALLBACK_API */ + + /* Init KEEPALIVE timer */ + pcb->keep_idle = TCP_KEEPIDLE_DEFAULT; + +#if LWIP_TCP_KEEPALIVE + pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT; + pcb->keep_cnt = TCP_KEEPCNT_DEFAULT; +#endif /* LWIP_TCP_KEEPALIVE */ + + pcb->keep_cnt_sent = 0; //���ķ��ʹ��� + } + return pcb; +} + +/** + * Creates a new TCP protocol control block but doesn't place it on + * any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). + * + * @internal: Maybe there should be a idle TCP PCB list where these + * PCBs are put on. Port reservation using tcp_bind() is implemented but + * allocated pcbs that are not bound can't be killed automatically if wanting + * to allocate a pcb with higher prio (@see tcp_kill_prio()) + * + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new(void) +{ + return tcp_alloc(TCP_PRIO_NORMAL); +} + +/** + * Used to specify the argument that should be passed callback + * functions. + *����ƿ��callback_arg�ֶ�ע���û���ݣ���tcp_recv�Ⱥ���ص�ʱ�� +* ���ֶν���Ϊ����ݸ�������� + * @param pcb tcp_pcb to set the callback argument + * @param arg void pointer argument to pass to callback functions + */ +void +tcp_arg(struct tcp_pcb *pcb, void *arg) +{ + pcb->callback_arg = arg; +} +#if LWIP_CALLBACK_API + +/** + * Used to specify the function that should be called when a TCP + * connection receives data. + *����ƿ��recv�ֶ�ע�ắ���յ����ʱ�ص� + * @param pcb tcp_pcb to set the recv callback + * @param recv callback function to call for this pcb when data is received + */ +void +tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv) +{ + pcb->recv = recv; +} + +/** + * Used to specify the function that should be called when TCP data + * has been successfully delivered to the remote host. + *����ƿ�send �ֶ�ע�ắ����ݷ��ͳɹ���ص� + * @param pcb tcp_pcb to set the sent callback + * @param sent callback function to call for this pcb when data is successfully sent + */ +void +tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent) +{ + pcb->sent = sent; +} + +/** + * Used to specify the function that should be called when a fatal error + * has occured on the connection. + *����ƿ�err �ֶ�ע�ắ�����������ص� + * @param pcb tcp_pcb to set the err callback + * @param err callback function to call for this pcb when a fatal error + * has occured on the connection + */ +void +tcp_err(struct tcp_pcb *pcb, tcp_err_fn err) +{ + pcb->errf = err; +} + +/** + * Used for specifying the function that should be called when a + * LISTENing connection has been connected to another host. + *����ƿ��accept�ֶ�ע�ắ����������ʱ�ص� + * @param pcb tcp_pcb to set the accept callback + * @param accept callback function to call for this pcb when LISTENing + * connection has been connected to another host + */ +void +tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept) +{ + pcb->accept = accept; +} +#endif /* LWIP_CALLBACK_API */ + + +/** + * Used to specify the function that should be called periodically + * from TCP. The interval is specified in terms of the TCP coarse + * timer interval, which is called twice a second. + *����ƿ��POLL�ֶ�ע�ắ��ú��������Ա����� + */ +void +tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval) +{ +#if LWIP_CALLBACK_API + pcb->poll = poll; +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(poll); +#endif /* LWIP_CALLBACK_API */ + pcb->pollinterval = interval; +} + +/** + * Purges a TCP PCB. Removes any buffered data and frees the buffer memory + * (pcb->ooseq, pcb->unsent and pcb->unacked are freed). + * + * @param pcb tcp_pcb to purge. The pcb itself is not deallocated! + */ +void +tcp_pcb_purge(struct tcp_pcb *pcb) +{ + if (pcb->state != CLOSED && + pcb->state != TIME_WAIT && + pcb->state != LISTEN) { + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n")); + +#if TCP_LISTEN_BACKLOG + if (pcb->state == SYN_RCVD) { + /* Need to find the corresponding listen_pcb and decrease its accepts_pending */ + struct tcp_pcb_listen *lpcb; + LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL", + tcp_listen_pcbs.listen_pcbs != NULL); + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if ((lpcb->local_port == pcb->local_port) && + (ip_addr_isany(&lpcb->local_ip) || + ip_addr_cmp(&pcb->local_ip, &lpcb->local_ip))) { + /* port and address of the listen pcb match the timed-out pcb */ + LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending", + lpcb->accepts_pending > 0); + lpcb->accepts_pending--; + break; + } + } + } +#endif /* TCP_LISTEN_BACKLOG */ + + + if (pcb->refused_data != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n")); + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + if (pcb->unsent != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n")); + } + if (pcb->unacked != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n")); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); + } + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; +#endif /* TCP_QUEUE_OOSEQ */ + + /* Stop the retransmission timer as it will expect data on unacked + queue if it fires */ + pcb->rtime = -1; + + tcp_segs_free(pcb->unsent); + tcp_segs_free(pcb->unacked); + pcb->unacked = pcb->unsent = NULL; +#if TCP_OVERSIZE + pcb->unsent_oversize = 0; +#endif /* TCP_OVERSIZE */ + } +} + +/** + * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first. + * + * @param pcblist PCB list to purge. + * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated! + */ +void +tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb) +{ + TCP_RMV(pcblist, pcb); + + tcp_pcb_purge(pcb); + + /* if there is an outstanding delayed ACKs, send it */ + if (pcb->state != TIME_WAIT && + pcb->state != LISTEN && + pcb->flags & TF_ACK_DELAY) { + pcb->flags |= TF_ACK_NOW; + tcp_output(pcb); + } + + if (pcb->state != LISTEN) { + LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL); + LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL); +#if TCP_QUEUE_OOSEQ + LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL); +#endif /* TCP_QUEUE_OOSEQ */ + } + + pcb->state = CLOSED; + + LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane()); +} + +/** + * Calculates a new initial sequence number for new connections. + * + * @return u32_t pseudo random sequence number + */ +u32_t +tcp_next_iss(void) +{ + static u32_t iss = 6510; + + iss += tcp_ticks; /* XXX */ + return iss; +} + +#if TCP_CALCULATE_EFF_SEND_MSS +/** + * Calcluates the effective send mss that can be used for a specific IP address + * by using ip_route to determin the netif used to send to the address and + * calculating the minimum of TCP_MSS and that netif's mtu (if set). + */ +u16_t +tcp_eff_send_mss(u16_t sendmss, ip_addr_t *addr) +{ + u16_t mss_s; + struct netif *outif; + + outif = ip_route(addr); + if ((outif != NULL) && (outif->mtu != 0)) { + mss_s = outif->mtu - IP_HLEN - TCP_HLEN; + /* RFC 1122, chap 4.2.2.6: + * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize + * We correct for TCP options in tcp_write(), and don't support IP options. + */ + sendmss = LWIP_MIN(sendmss, mss_s); + } + return sendmss; +} +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +const char* +tcp_debug_state_str(enum tcp_state s) +{ + return tcp_state_str[s]; +} + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +/** + * Print a tcp header for debugging purposes. + * + * @param tcphdr pointer to a struct tcp_hdr + */ +void +tcp_debug_print(struct tcp_hdr *tcphdr) +{ + LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(tcphdr->src), ntohs(tcphdr->dest))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n", + ntohl(tcphdr->seqno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n", + ntohl(tcphdr->ackno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (", + TCPH_HDRLEN(tcphdr), + TCPH_FLAGS(tcphdr) >> 5 & 1, + TCPH_FLAGS(tcphdr) >> 4 & 1, + TCPH_FLAGS(tcphdr) >> 3 & 1, + TCPH_FLAGS(tcphdr) >> 2 & 1, + TCPH_FLAGS(tcphdr) >> 1 & 1, + TCPH_FLAGS(tcphdr) & 1, + ntohs(tcphdr->wnd))); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_DEBUG, ("), win)\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n", + ntohs(tcphdr->chksum), ntohs(tcphdr->urgp))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); +} + +/** + * Print a tcp state for debugging purposes. + * + * @param s enum tcp_state to print + */ +void +tcp_debug_print_state(enum tcp_state s) +{ + LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s])); +} + +/** + * Print tcp flags for debugging purposes. + * + * @param flags tcp flags, all active flags are printed + */ +void +tcp_debug_print_flags(u8_t flags) +{ + if (flags & TCP_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("FIN ")); + } + if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("SYN ")); + } + if (flags & TCP_RST) { + LWIP_DEBUGF(TCP_DEBUG, ("RST ")); + } + if (flags & TCP_PSH) { + LWIP_DEBUGF(TCP_DEBUG, ("PSH ")); + } + if (flags & TCP_ACK) { + LWIP_DEBUGF(TCP_DEBUG, ("ACK ")); + } + if (flags & TCP_URG) { + LWIP_DEBUGF(TCP_DEBUG, ("URG ")); + } + if (flags & TCP_ECE) { + LWIP_DEBUGF(TCP_DEBUG, ("ECE ")); + } + if (flags & TCP_CWR) { + LWIP_DEBUGF(TCP_DEBUG, ("CWR ")); + } + LWIP_DEBUGF(TCP_DEBUG, ("\n")); +} + +/** + * Print all tcp_pcbs in every list for debugging purposes. + */ +void +tcp_debug_print_pcbs(void) +{ + struct tcp_pcb *pcb; + LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n")); + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n")); + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n")); + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } +} + +/** + * Check state consistency of the tcp_pcb lists. + */ +s16_t +tcp_pcbs_sane(void) +{ + struct tcp_pcb *pcb; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + } + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + } + return 1; +} +#endif /* TCP_DEBUG */ + +#endif /* LWIP_TCP */ diff --git a/app/lwip/core/tcp_in.c b/app/lwip/core/tcp_in.c new file mode 100644 index 00000000..2f4a1c42 --- /dev/null +++ b/app/lwip/core/tcp_in.c @@ -0,0 +1,1618 @@ +/** + * @file + * Transmission Control Protocol, incoming traffic + * + * The input processing functions of the TCP layer. + * + * These functions are generally called in the order (ip_input() ->) + * tcp_input() -> * tcp_process() -> tcp_receive() (-> application). + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp_impl.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" + +/* These variables are global to all functions involved in the input + processing of TCP segments. They are set by the tcp_input() + function. */ +static struct tcp_seg inseg; //tcp_segṹıĶ +static struct tcp_hdr *tcphdr; //ĶTCPײ +static struct ip_hdr *iphdr; //IPݰײ +static u32_t seqno, ackno; //TCPײֶȷϺֶ +static u8_t flags; //ײ־ֶ +static u16_t tcplen; //TCPij + +static u8_t recv_flags; //ǰĴ +static struct pbuf *recv_data; //Ķpbuf + +struct tcp_pcb *tcp_input_pcb; //ǰĿƿ + +/* Forward declarations. */ +static err_t tcp_process(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +static void tcp_receive(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +static void tcp_parseopt(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; + +static err_t tcp_listen_input(struct tcp_pcb_listen *pcb)ICACHE_FLASH_ATTR; +static err_t tcp_timewait_input(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; + +/** + * The initial input processing of TCP. It verifies the TCP header, demultiplexes + * the segment between the PCBs and passes it on to tcp_process(), which implements + * the TCP finite state machine. This function is called by the IP layer (in + * ip_input()). + * + * @param p received TCP segment to process (p->payload pointing to the IP header) + * @param inp network interface on which this segment was received + */ + /** + * TCPʼ봦֤TCPͷIP + + * @p:յTCP(ָIPͷĸ) + * @inp:նεӿ + */ +void +tcp_input(struct pbuf *p, struct netif *inp) +{ + struct tcp_pcb *pcb, *prev; + struct tcp_pcb_listen *lpcb; +#if SO_REUSE + struct tcp_pcb *lpcb_prev = NULL; + struct tcp_pcb_listen *lpcb_any = NULL; +#endif /* SO_REUSE */ + u8_t hdrlen; + err_t err; + + PERF_START; + + TCP_STATS_INC(tcp.recv); //״̬1 + snmp_inc_tcpinsegs(); //tcpμ1 + + iphdr = (struct ip_hdr *)p->payload;// pointer to the actual data in the buffer + /* + *ͷ(IHL)4λIPЭͷijȣָIPv4Эͷȵֽٸ32λ + *IPv4İͷܰɱĿѡ ֶοȷIPv4ݱݲֵƫ + *IPv4ͷС20ֽڣIHLֶεСֵʮƱʾ5 (5x4 = 20ֽ) + *˵ʾİͷֽ4ֽڵı + */ + tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); + +#if TCP_INPUT_DEBUG + tcp_debug_print(tcphdr); +#endif + + /* remove header from payload */ + if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) { + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); + TCP_STATS_INC(tcp.lenerr);//󳤶ȼ + TCP_STATS_INC(tcp.drop);//ֹ + snmp_inc_tcpinerrs(); + pbuf_free(p);//ͷbuffer + return; + } + + /* Don't even process incoming broadcasts/multicasts. */ + if (ip_addr_isbroadcast(¤t_iphdr_dest, inp) || + ip_addr_ismulticast(¤t_iphdr_dest)) { + TCP_STATS_INC(tcp.proterr);//Э + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } + +#if CHECKSUM_CHECK_TCP + /* Verify TCP checksum. */ + if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(), + IP_PROTO_TCP, p->tot_len) != 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", + inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(), + IP_PROTO_TCP, p->tot_len))); +#if TCP_DEBUG + tcp_debug_print(tcphdr); +#endif /* TCP_DEBUG */ + TCP_STATS_INC(tcp.chkerr);//У + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } +#endif + + /* Move the payload pointer in the pbuf so that it points to the + TCP data instead of the TCP header. */ + hdrlen = TCPH_HDRLEN(tcphdr);//ͷij + if(pbuf_header(p, -(hdrlen * 4))){//TCPͷ0Ϊɹ + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); + TCP_STATS_INC(tcp.lenerr);//tcpȴ + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } + + /* Convert fields in TCP header to host byte order. */ + tcphdr->src = ntohs(tcphdr->src); //תԴַ + tcphdr->dest = ntohs(tcphdr->dest); //תĿĵַ + seqno = tcphdr->seqno = ntohl(tcphdr->seqno); //תк + ackno = tcphdr->ackno = ntohl(tcphdr->ackno); //תӦ + tcphdr->wnd = ntohs(tcphdr->wnd); //תtcp + + flags = TCPH_FLAGS(tcphdr);//õtcp headerı־ + /* + *־3λֶΣ + * λ1λ + * ֶλ1λȡֵ0ݱֶΣ1ݱֶܷΣ + * λ1λȡֵ0ݰûа1ݰиİ + */ + tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0);//TCP_FIN TCP_SYN λ10 + + /* Demultiplex an incoming segment. First, we check if it is destined + for an active connection. ȣǷһҪһ*/ + //////////////////////////////////////////////////////////////////////////////////////// + prev = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {//б + LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src) && + ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest)) {//صĵַ + + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); + if (prev != NULL) {//ǰһڵ㲻Ϊ + prev->next = pcb->next; + pcb->next = tcp_active_pcbs; + tcp_active_pcbs = pcb;//pcbǰ + } + LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); + break; + } + prev = pcb;//prevָpcb + } + + if (pcb == NULL) { + /* If it did not go to an active connection, we check the connections + in the TIME-WAIT state. */ + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {//ȴ״̬µpcb + LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src) && + ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest)) { + /* We don't really care enough to move this PCB to the front + of the list since we are not very likely to receive that + many segments for connections in TIME-WAIT. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); + tcp_timewait_input(pcb);//tcp timewait İ + pbuf_free(p); + return; + } + } + + /* Finally, if we still did not get a match, we check all PCBs that + are LISTENing for incoming connections. */ + prev = NULL; + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {//״̬еpcb + if (lpcb->local_port == tcphdr->dest) { +#if SO_REUSE + if (ip_addr_cmp(&(lpcb->local_ip), ¤t_iphdr_dest)) { + /* found an exact match */ + break; + } else if(ip_addr_isany(&(lpcb->local_ip))) { + /* found an ANY-match */ + lpcb_any = lpcb; + lpcb_prev = prev; + } +#else /* SO_REUSE */ + if (ip_addr_cmp(&(lpcb->local_ip), ¤t_iphdr_dest) || + ip_addr_isany(&(lpcb->local_ip))) { + /* found a match */ + break; + } +#endif /* SO_REUSE */ + } + prev = (struct tcp_pcb *)lpcb; + } +#if SO_REUSE + /* first try specific local IP */ + if (lpcb == NULL) { + /* only pass to ANY if no specific local IP has been found */ + lpcb = lpcb_any; + prev = lpcb_prev; + } +#endif /* SO_REUSE */ + if (lpcb != NULL) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + if (prev != NULL) { + ((struct tcp_pcb_listen *)prev)->next = lpcb->next; + /* our successor is the remainder of the listening list */ + lpcb->next = tcp_listen_pcbs.listen_pcbs; + /* put this listening pcb at the head of the listening list */ + tcp_listen_pcbs.listen_pcbs = lpcb; + } + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); + tcp_listen_input(lpcb);//tcpݰ + pbuf_free(p); + return; + } + } + +#if TCP_INPUT_DEBUG + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")); +#endif /* TCP_INPUT_DEBUG */ + + + if (pcb != NULL) { + /* The incoming segment belongs to a connection. */ +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + + /* Set up a tcp_seg structure. */ + inseg.next = NULL; + inseg.len = p->tot_len; + inseg.p = p; + inseg.tcphdr = tcphdr; + + recv_data = NULL; + recv_flags = 0; + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + /* Notify again application with data previously received. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); + TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);//pcb + if (err == ERR_OK) { + pcb->refused_data = NULL; + } else if ((err == ERR_ABRT) || (tcplen > 0)) { + /* if err == ERR_ABRT, 'pcb' is already deallocated */ + /* Drop incoming packets because pcb is "full" (only if the incoming + segment contains data). */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); + TCP_STATS_INC(tcp.drop);//tcp + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } + } + tcp_input_pcb = pcb;//¼ǰĴĿƿ + err = tcp_process(pcb);// + /* A return value of ERR_ABRT means that tcp_abort() was called + and that the pcb has been freed. If so, we don't do anything. */ + if (err != ERR_ABRT) { + if (recv_flags & TF_RESET) { + /* TF_RESET means that the connection was reset by the other + end. We then call the error callback to inform the + application that the connection is dead before we + deallocate the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST); + tcp_pcb_remove(&tcp_active_pcbs, pcb);//ɾpcbбеpcb + memp_free(MEMP_TCP_PCB, pcb); + } else if (recv_flags & TF_CLOSED) { + /* The connection has been closed and we will deallocate the + PCB. */ + if (!(pcb->flags & TF_RXCLOSED)) { + /* Connection closed although the application has only shut down the + tx side: call the PCB's err callback and indicate the closure to + ensure the application doesn't continue using the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD); + } + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + err = ERR_OK; + /* If the application has registered a "sent" function to be + called when new send buffer space is available, we call it + now. */ + if (pcb->acked > 0) { + TCP_EVENT_SENT(pcb, pcb->acked, err);//ݱȷϣصûsend + if (err == ERR_ABRT) { + goto aborted; + } + } + + if (recv_data != NULL) {//ݽյ + LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL); + if (pcb->flags & TF_RXCLOSED) { + /* received data although already closed -> abort (send RST) to + notify the remote host that not all data has been processed */ + pbuf_free(recv_data); + tcp_abort(pcb); + goto aborted; + } + + //PSH־ PSH λ + //PSH=1ʱҪͷϷ͸÷ֶΣ + //շĽĽӦò㣬д + + if (flags & TCP_PSH) { + recv_data->flags |= PBUF_FLAG_PUSH;//bufferӦ + } + + /* Notify application that data has been received. */ + TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); + if (err == ERR_ABRT) { + goto aborted; + } + + /* If the upper layer can't receive this data, store it */ + if (err != ERR_OK) { + pcb->refused_data = recv_data; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); + } + } + + /* If a FIN segment was received, we call the callback + function with a NULL buffer to indicate EOF. */ + if (recv_flags & TF_GOT_FIN) { + /* correct rcv_wnd as the application won't call tcp_recved() + for the FIN's seqno */ + if (pcb->rcv_wnd != TCP_WND) { + pcb->rcv_wnd++; + } + + TCP_EVENT_CLOSED(pcb, err); + if (err == ERR_ABRT) { + goto aborted; + } + } + + tcp_input_pcb = NULL;//ȫֱ + /* Try to send something out. */ + tcp_output(pcb);// +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + } + } + /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()). + Below this line, 'pcb' may not be dereferenced! */ +aborted: + tcp_input_pcb = NULL; + recv_data = NULL; + + /* give up our reference to inseg.p */ + if (inseg.p != NULL) + { + pbuf_free(inseg.p);//ͷbuffer + inseg.p = NULL; + } + } else { + + /* If no matching PCB was found, send a TCP RST (reset) to the + sender. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); + if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { + TCP_STATS_INC(tcp.proterr);//Э + TCP_STATS_INC(tcp.drop);//tcp + tcp_rst(ackno, seqno + tcplen, + ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src);//TCPλ + } + pbuf_free(p); + } + + LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); + PERF_STOP("tcp_input"); +} + +/** + * Called by tcp_input() when a segment arrives for a listening + * connection (from tcp_input()). + * + * @param pcb the tcp_pcb_listen for which a segment arrived + * @return ERR_OK if the segment was processed + * another err_t on error + * + * @note the return value is not (yet?) used in tcp_input() + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ + /* +*LISTEN״̬Ŀƿøú +*ͨǷһ˿ڲͻSYN +* +*/ +static err_t +tcp_listen_input(struct tcp_pcb_listen *pcb) +{ + struct tcp_pcb *npcb; + struct tcp_pcb *pactive_pcb; + u8_t active_pcb_num = 0; + err_t rc; + + /* In the LISTEN state, we check for incoming SYN segments, + creates a new PCB, and responds with a SYN|ACK. */ + if (flags & TCP_ACK) { + /* For incoming segments with the ACK flag set, respond with a + RST. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n")); + tcp_rst(ackno + 1, seqno + tcplen, + ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + } else if (flags & TCP_SYN) {//յSYN + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest)); +#if TCP_LISTEN_BACKLOG + if (pcb->accepts_pending >= pcb->backlog) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest)); + return ERR_ABRT; + } +#endif /* TCP_LISTEN_BACKLOG */ + for(pactive_pcb = tcp_active_pcbs; pactive_pcb != NULL; pactive_pcb = pactive_pcb->next) + active_pcb_num ++; + if (active_pcb_num == MEMP_NUM_TCP_PCB){ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: exceed the number of active TCP connections\n")); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + npcb = tcp_alloc(pcb->prio);//ƿ + /* If a new PCB could not be created (probably due to lack of memory), + we don't do anything, but rely on the sender will retransmit the + SYN at a time when we have more memory available. */ + if (npcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n")); + TCP_STATS_INC(tcp.memerr);//TCPڴ + return ERR_MEM; + } +#if TCP_LISTEN_BACKLOG + pcb->accepts_pending++; +#endif /* TCP_LISTEN_BACKLOG */ + /* Set up the new PCB. */ + //ƿص4ֶ + ip_addr_copy(npcb->local_ip, current_iphdr_dest); + npcb->local_port = pcb->local_port; + ip_addr_copy(npcb->remote_ip, current_iphdr_src); + npcb->remote_port = tcphdr->src; + + //ƿֶ + npcb->state = SYN_RCVD;//״̬ + npcb->rcv_nxt = seqno + 1;//һ + npcb->rcv_ann_right_edge = npcb->rcv_nxt; + npcb->snd_wnd = tcphdr->wnd;//÷ʹ + npcb->ssthresh = npcb->snd_wnd; + npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */ + npcb->callback_arg = pcb->callback_arg; +#if LWIP_CALLBACK_API + npcb->accept = pcb->accept; +#endif /* LWIP_CALLBACK_API */ + /* inherit socket options */ + npcb->so_options = pcb->so_options & SOF_INHERITED; + /* Register the new PCB so that we can begin receiving segments + for it. */ + TCP_REG(&tcp_active_pcbs, npcb); + + /* Parse any options in the SYN. */ + tcp_parseopt(npcb); +#if TCP_CALCULATE_EFF_SEND_MSS + npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + snmp_inc_tcppassiveopens(); + + /* Send a SYN|ACK together with the MSS option. */ + rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK); + if (rc != ERR_OK) {//ͷ¿ƿ + tcp_abandon(npcb, 0); + return rc; + } + return tcp_output(npcb);//ͱ + } + return ERR_OK; +} + +/** + * Called by tcp_input() when a segment arrives for a connection in + * TIME_WAIT. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ + /* +*TIME_WAIT״̬ĿƿøúյıĶΣ +*״̬£رӵֹѾڵȴ2MSLʱ +*״̬µıĶеľݣֱɾɡ +*ҪͷACK +*/ +static err_t +tcp_timewait_input(struct tcp_pcb *pcb) +{ + + if (flags & TCP_RST) { //RSTλֱӷ + return ERR_OK; + } + + if (flags & TCP_SYN) { //SYNϢݱڽմڣͷRST + + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { + + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + return ERR_OK; + } + } else if (flags & TCP_FIN) { //İFINϢ + + pcb->tmr = tcp_ticks; //λȴ2MSLʱ䣬ƿµȴ2MSL + } + + if ((tcplen > 0)) { //ݵıĻڽմSYN + pcb->flags |= TF_ACK_NOW;//һACK + return tcp_output(pcb); + } + return ERR_OK; +} + +/** + * Implements the TCP state machine. Called by tcp_input. In some + * states tcp_receive() is called to receive data. The tcp_seg + * argument will be freed by the caller (tcp_input()) unless the + * recv_data pointer in the pcb is set. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_process(struct tcp_pcb *pcb) +{ + struct tcp_seg *rseg; + u8_t acceptable = 0; + err_t err; + + err = ERR_OK; + + /* Process incoming RST segments. */ + if (flags & TCP_RST) { + /* First, determine if the reset is acceptable. */ + if (pcb->state == SYN_SENT) { + if (ackno == pcb->snd_nxt) { + acceptable = 1; + } + } else { + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt+pcb->rcv_wnd)) { + acceptable = 1; + } + } + + if (acceptable) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n")); + LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED); + recv_flags |= TF_RESET; + pcb->flags &= ~TF_ACK_DELAY; + return ERR_RST; + } else { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + return ERR_OK; + } + } + + if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) { + /* Cope with new connection attempt after remote end crashed */ + tcp_ack_now(pcb); + return ERR_OK; + } + + if ((pcb->flags & TF_RXCLOSED) == 0) { + /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */ + pcb->tmr = tcp_ticks; + } + pcb->keep_cnt_sent = 0; + + tcp_parseopt(pcb); + + /* Do different things depending on the TCP state. */ + switch (pcb->state) { + case SYN_SENT: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno, + pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno))); + /* received SYN ACK with expected sequence number? */ + if ((flags & TCP_ACK) && (flags & TCP_SYN) + && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) { + pcb->snd_buf++; + pcb->rcv_nxt = seqno + 1; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; + pcb->lastack = ackno; + pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */ + pcb->state = ESTABLISHED; + +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + /* Set ssthresh again after changing pcb->mss (already set in tcp_connect + * but for the default value of pcb->mss) */ + pcb->ssthresh = pcb->mss * 10; + + pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0)); + --pcb->snd_queuelen; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + rseg = pcb->unacked; + pcb->unacked = rseg->next; + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) + pcb->rtime = -1; + else { + pcb->rtime = 0; + pcb->nrtx = 0; + } + + tcp_seg_free(rseg); + + /* Call the user specified function to call when sucessfully + * connected. */ + TCP_EVENT_CONNECTED(pcb, ERR_OK, err); + if (err == ERR_ABRT) { + return ERR_ABRT; + } + tcp_ack_now(pcb); + } + /* received ACK? possibly a half-open connection */ + else if (flags & TCP_ACK) { + /* send a RST to bring the other side in a non-synchronized state. */ + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + } + break; + case SYN_RCVD: + if (flags & TCP_ACK) { + /* expected ACK number? */ + if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { + u16_t old_cwnd; + pcb->state = ESTABLISHED; + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); +#if LWIP_CALLBACK_API + LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL); +#endif + /* Call the accept function. */ + TCP_EVENT_ACCEPT(pcb, ERR_OK, err); + if (err != ERR_OK) { + /* If the accept function returns with an error, we abort + * the connection. */ + /* Already aborted? */ + if (err != ERR_ABRT) { + tcp_abort(pcb); + } + return ERR_ABRT; + } + old_cwnd = pcb->cwnd; + /* If there was any data contained within this ACK, + * we'd better pass it on to the application as well. */ + tcp_receive(pcb); + + /* Prevent ACK for SYN to generate a sent event */ + if (pcb->acked != 0) { + pcb->acked--; + } + + pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + + if (recv_flags & TF_GOT_FIN) { + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + } else { + /* incorrect ACK number, send RST */ + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + } + } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) { + /* Looks like another copy of the SYN - retransmit our SYN-ACK */ + tcp_rexmit(pcb); + } + break; + case CLOSE_WAIT: + /* FALLTHROUGH */ + case ESTABLISHED: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { /* passive close */ + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + break; + case FIN_WAIT_1: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { + if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { + LWIP_DEBUGF(TCP_DEBUG, + ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + tcp_ack_now(pcb); + pcb->state = CLOSING; + } + } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { + pcb->state = FIN_WAIT_2; + } + break; + case FIN_WAIT_2: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case CLOSING: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case LAST_ACK: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */ + recv_flags |= TF_CLOSED; + } + break; + default: + break; + } + return ERR_OK; +} + +#if TCP_QUEUE_OOSEQ +/** + * Insert segment into the list (segments covered with new one will be deleted) + * + * Called from tcp_receive() + */ +static void ICACHE_FLASH_ATTR +tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next) +{ + struct tcp_seg *old_seg; + + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + /* received segment overlaps all following segments */ + tcp_segs_free(next); + next = NULL; + } + else { + /* delete some following segments + oos queue may have segments with FIN flag */ + while (next && + TCP_SEQ_GEQ((seqno + cseg->len), + (next->tcphdr->seqno + next->len))) { + /* cseg with FIN already processed */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN); + } + old_seg = next; + next = next->next; + tcp_seg_free(old_seg); + } + if (next && + TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + cseg->len = (u16_t)(next->tcphdr->seqno - seqno); + pbuf_realloc(cseg->p, cseg->len); + } + } + cseg->next = next; +} +#endif /* TCP_QUEUE_OOSEQ */ + +/** + * Called by tcp_process. Checks if the given segment is an ACK for outstanding + * data, and if so frees the memory of the buffered data. Next, is places the + * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment + * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until + * i it has been removed from the buffer. + * + * If the incoming segment constitutes an ACK for a segment that was used for RTT + * estimation, the RTT is estimated here as well. + * + * Called from tcp_process(). + */ +static void +tcp_receive(struct tcp_pcb *pcb) +{ + struct tcp_seg *next; +#if TCP_QUEUE_OOSEQ + struct tcp_seg *prev, *cseg; +#endif /* TCP_QUEUE_OOSEQ */ + struct pbuf *p; + s32_t off; + s16_t m; + u32_t right_wnd_edge; + u16_t new_tot_len; + int found_dupack = 0; + + if (flags & TCP_ACK) {//İACK + right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;//ʹ + Ӧ󴰿ڸ + + // first /* Update window. */ + /*seqno > snd_wl1ֹ̲ôָ; + *seqno = snd_wl1ackno > snd_wl2;ʱԷûзݣֻյݵȷ; + *ackno = snd_wl2ұײбsnd_wndĴ.Ӧֵ + */ + if (TCP_SEQ_LT(pcb->snd_wl1, seqno) || + (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) || + (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) { + pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wl1 = seqno; + pcb->snd_wl2 = ackno; + if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) { + pcb->persist_backoff = 0;//ʱ˳ + } + LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd)); +#if TCP_WND_DEBUG + } else { + if (pcb->snd_wnd != tcphdr->wnd) { + LWIP_DEBUGF(TCP_WND_DEBUG, + ("tcp_receive: no window update lastack %"U32_F" ackno %" + U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n", + pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2)); + } +#endif /* TCP_WND_DEBUG */ + } + + /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a + * duplicate ack if: + * 1) It doesn't ACK new data ûȷ + * 2) length of received packet is zero (i.e. no payload) Ķκ + * 3) the advertised window hasn't changed شûи + * 4) There is outstanding unacknowledged data (retransmission timer running)ݵȴȷ + * 5) The ACK is == biggest ACK sequence number so far seen (snd_una) ackno = lastack + * + * If it passes all five, should process as a dupack: + * a) dupacks < 3: do nothing + * b) dupacks == 3: fast retransmit + * c) dupacks > 3: increase cwnd + * + * If it only passes 1-3, should reset dupack counter (and add to + * stats, which we don't do in lwIP) + * + * If it only passes 1, should reset dupack counter + * + */ + + /* Clause 1 */ + if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {//ظACK? + pcb->acked = 0; + /* Clause 2 */ + if (tcplen == 0) { + /* Clause 3 */ + if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){ + /* Clause 4 */ + if (pcb->rtime >= 0) { + /* Clause 5 */ + if (pcb->lastack == ackno) { + found_dupack = 1; + if (pcb->dupacks + 1 > pcb->dupacks) + ++pcb->dupacks; + if (pcb->dupacks > 3) { + /* Inflate the congestion window, but not if it means that + the value overflows. */ + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + } else if (pcb->dupacks == 3) {//ظACK + /* Do fast retransmit */ + tcp_rexmit_fast(pcb); + } + } + } + } + } + /* If Clause (1) or more is true, but not a duplicate ack, reset + * count of consecutive duplicate acks */ + if (!found_dupack) { + pcb->dupacks = 0; + } + } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){//acknolastack+1snd_nxt֮䣬жϷʹ + /* We come here when the ACK acknowledges new data. */ + + if (pcb->flags & TF_INFR) { + pcb->flags &= ~TF_INFR;// Reset the "IN Fast Retransmit" flag,since we are no longer in fast retransmit + pcb->cwnd = pcb->ssthresh;//Reset the congestion window to the "slow start threshold". + } + + /* Reset the number of retransmissions. */ + pcb->nrtx = 0; + + /* Reset the retransmission time-out. */ + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + /* Update the send buffer space. Diff between the two can never exceed 64K? */ + pcb->acked = (u16_t)(ackno - pcb->lastack); + + pcb->snd_buf += pcb->acked; + + /* Reset the fast retransmit variables. */ + pcb->dupacks = 0; + pcb->lastack = ackno; + + /* Update the congestion control variables (cwnd and + ssthresh). */ + if (pcb->state >= ESTABLISHED) {//״̬Ϊӱ־ + if (pcb->cwnd < pcb->ssthresh) { + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd)); + } else { + u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd); + if (new_cwnd > pcb->cwnd) { + pcb->cwnd = new_cwnd; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd)); + } + } + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n", + ackno, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno): 0, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0)); + + /* Remove segment from the unacknowledged list if the incoming + ACK acknowlegdes them. + *ͷunackedϱȷϵıĶΣ + *ֱunackedΪֹͣ*/ + while (pcb->unacked != NULL && + TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked), ackno)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n", + ntohl(pcb->unacked->tcphdr->seqno), + ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked))); + + next = pcb->unacked;//pcb unacked־ + pcb->unacked = pcb->unacked->next;//pcb unacked һ־ + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) { + pcb->acked--; + } + + pcb->snd_queuelen -= pbuf_clen(next->p);//pbufs + tcp_seg_free(next);//ͷtcp + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + } + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) //ݵȴȷ + pcb->rtime = -1; //ֹͣشʱ + else + pcb->rtime = 0; //λشʱ + + pcb->polltmr = 0; + } else { + /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */ + pcb->acked = 0; + } + + /* We go through the ->unsent list to see if any of the segments + on the list are acknowledged by the ACK. This may seem + strange since an "unsent" segment shouldn't be acked. The + rationale is that lwIP puts all outstanding segments on the + ->unsent list after a retransmission, so these segments may + in fact have been sent once. */ + /** unsentǷܱacknoȷϵıĶΣͷ**/ + while (pcb->unsent != NULL && + TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n", + ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent))); + + next = pcb->unsent;//pcbδͱ־ + pcb->unsent = pcb->unsent->next;//δ͵һ + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) { + pcb->acked--; + } + pcb->snd_queuelen -= pbuf_clen(next->p);//pbufĸ + tcp_seg_free(next);//ͷŶ + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) {//г + LWIP_ASSERT("tcp_receive: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + } + /* End of ACK for new data processing. */ + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n", + pcb->rttest, pcb->rtseq, ackno)); + + /* RTT estimation calculations. This is done by checking if the + incoming segment acknowledges the segment we use to take a + round-trip time measurement. */ + if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {//RTTڽҸñĶαȷ + /* diff between this shouldn't exceed 32K since this are tcp timer ticks + and a round-trip shouldn't be that long... */ + m = (s16_t)(tcp_ticks - pcb->rttest);//Mֵ + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n", + m, m * TCP_SLOW_INTERVAL)); + + /* This is taken directly from VJs original code in his paper RTT㹫ʽ*/ + m = m - (pcb->sa >> 3); + pcb->sa += m; + if (m < 0) { + m = -m; + } + m = m - (pcb->sv >> 2); + pcb->sv += m; + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n", + pcb->rto, pcb->rto * TCP_SLOW_INTERVAL)); + + pcb->rttest = 0; + } + } + + /* If the incoming segment contains data, we must process it + further. */ + if (tcplen > 0) { + /* This code basically does three things: + + +) If the incoming segment contains data that is the next + in-sequence data, this data is passed to the application. This + might involve trimming the first edge of the data. The rcv_nxt + variable and the advertised window are adjusted. + + +) If the incoming segment has data that is above the next + sequence number expected (->rcv_nxt), the segment is placed on + the ->ooseq queue. This is done by finding the appropriate + place in the ->ooseq queue (which is ordered by sequence + number) and trim the segment in both ends if needed. An + immediate ACK is sent to indicate that we received an + out-of-sequence segment. + + +) Finally, we check if the first segment on the ->ooseq queue + now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If + rcv_nxt > ooseq->seqno, we must trim the first edge of the + segment on ->ooseq before we adjust rcv_nxt. The data in the + segments that are now on sequence are chained onto the + incoming segment so that we only need to call the application + once. + */ + + /* First, we check if we must trim the first edge. We have to do + this if the sequence number of the incoming segment is less + than rcv_nxt, and the sequence number plus the length of the + segment is larger than rcv_nxt. */ + /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/ + if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){// seqno < rcv_nxt < seqno + tcplen + /* Trimming the first edge is done by pushing the payload + pointer in the pbuf downwards. This is somewhat tricky since + we do not want to discard the full contents of the pbuf up to + the new starting point of the data since we have to keep the + TCP header which is present in the first pbuf in the chain. + + What is done is really quite a nasty hack: the first pbuf in + the pbuf chain is pointed to by inseg.p. Since we need to be + able to deallocate the whole pbuf, we cannot change this + inseg.p pointer to point to any of the later pbufs in the + chain. Instead, we point the ->payload pointer in the first + pbuf to data in one of the later pbufs. We also set the + inseg.data pointer to point to the right place. This way, the + ->p pointer will still point to the first pbuf, but the + ->p->payload pointer will point to data in another pbuf. + + After we are done with adjusting the pbuf pointers we must + adjust the ->data pointer in the seg and the segment + length.*/ + //ȥĶݱŵrcv_nxt + off = pcb->rcv_nxt - seqno; + p = inseg.p; + LWIP_ASSERT("inseg.p != NULL", inseg.p); + LWIP_ASSERT("insane offset!", (off < 0x7fff)); + if (inseg.p->len < off) { + LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off)); + new_tot_len = (u16_t)(inseg.p->tot_len - off); + while (p->len < off) { + off -= p->len; + /* KJM following line changed (with addition of new_tot_len var) + to fix bug #9076 + inseg.p->tot_len -= p->len; */ + p->tot_len = new_tot_len; + p->len = 0; + p = p->next; + } + if(pbuf_header(p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } else { + if(pbuf_header(inseg.p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } + inseg.len -= (u16_t)(pcb->rcv_nxt - seqno); + inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; + } + else { + if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){//seqno < rcv_nxt + /* the whole segment is < rcv_nxt */ + /* must be a duplicate of a packet that has already been correctly handled */ + //ĶݱžСrcv_nxt˱ظģ + //ֱԴӦACKĴ + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno)); + tcp_ack_now(pcb); + } + } + + /* The sequence number must be within the window (above rcv_nxt + and below rcv_nxt + rcv_wnd) in order to be further + processed. */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt + pcb->rcv_wnd - 1)){//rcv_nxt < seqno < rcv_nxt + rcv_wnd - 1,ڽշΧ + if (pcb->rcv_nxt == seqno) { + /* The incoming segment is the next in sequence. We check if + we have to trim the end of the segment and update rcv_nxt + and pass the data to the application. */ + tcplen = TCP_TCPLEN(&inseg);//㱨Ķγ + + if (tcplen > pcb->rcv_wnd) {//մڴСβض + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + inseg.len = pcb->rcv_wnd; + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } +#if TCP_QUEUE_OOSEQ + /* Received in-sequence data, adjust ooseq data if: + - FIN has been received or + - inseq overlaps with ooseq */ + if (pcb->ooseq != NULL) { + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: received in-order FIN, binning ooseq queue\n")); + /* Received in-order FIN means anything that was received + * out of order must now have been received in-order, so + * bin the ooseq queue */ + while (pcb->ooseq != NULL) { + struct tcp_seg *old_ooseq = pcb->ooseq; + pcb->ooseq = pcb->ooseq->next; + tcp_seg_free(old_ooseq); + } + } + else { + next = pcb->ooseq; + /* Remove all segments on ooseq that are covered by inseg already. + * FIN is copied from ooseq to inseg if present. */ + while (next && + TCP_SEQ_GEQ(seqno + tcplen, + next->tcphdr->seqno + next->len)) { + /* inseg cannot have FIN here (already processed above) */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN && + (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) { + TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN); + tcplen = TCP_TCPLEN(&inseg); + } + prev = next; + next = next->next; + tcp_seg_free(prev); + } + /* Now trim right side of inseg if it overlaps with the first + * segment on ooseq */ + if (next && + TCP_SEQ_GT(seqno + tcplen, + next->tcphdr->seqno)) { + /* inseg cannot have FIN here (already processed above) */ + inseg.len = (u16_t)(next->tcphdr->seqno - seqno); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n", + (seqno + tcplen) == next->tcphdr->seqno); + } + pcb->ooseq = next; + } + } +#endif /* TCP_QUEUE_OOSEQ */ + + pcb->rcv_nxt = seqno + tcplen; + + /* Update the receiver's (our) window. */ + LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen); + pcb->rcv_wnd -= tcplen; + + tcp_update_rcv_ann_wnd(pcb); + + /* If there is data in the segment, we make preparations to + pass this up to the application. The ->recv_data variable + is used for holding the pbuf that goes to the + application. The code for reassembling out-of-sequence data + chains its data on this pbuf as well. + + If the segment was a FIN, we set the TF_GOT_FIN flag that will + be used to indicate to the application that the remote side has + closed its end of the connection. */ + if (inseg.p->tot_len > 0) { + recv_data = inseg.p; + /* Since this pbuf now is the responsibility of the + application, we delete our reference to it so that we won't + (mistakingly) deallocate it. */ + inseg.p = NULL; + } + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n")); + recv_flags |= TF_GOT_FIN; + } + +#if TCP_QUEUE_OOSEQ + /* We now check if we have segments on the ->ooseq queue that + are now in sequence. */ + while (pcb->ooseq != NULL && + pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) { + + cseg = pcb->ooseq; + seqno = pcb->ooseq->tcphdr->seqno; + + pcb->rcv_nxt += TCP_TCPLEN(cseg); + LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n", + pcb->rcv_wnd >= TCP_TCPLEN(cseg)); + pcb->rcv_wnd -= TCP_TCPLEN(cseg); + + tcp_update_rcv_ann_wnd(pcb); + + if (cseg->p->tot_len > 0) { + /* Chain this pbuf onto the pbuf that we will pass to + the application. */ + if (recv_data) { + pbuf_cat(recv_data, cseg->p); + } else { + recv_data = cseg->p; + } + cseg->p = NULL; + } + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n")); + recv_flags |= TF_GOT_FIN; + if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */ + pcb->state = CLOSE_WAIT; + } + } + + pcb->ooseq = cseg->next; + tcp_seg_free(cseg); + } +#endif /* TCP_QUEUE_OOSEQ */ + + + /* Acknowledge the segment(s). */ + tcp_ack(pcb); + + } else { + /* We get here if the incoming segment is out-of-sequence. */ + tcp_send_empty_ack(pcb); +#if TCP_QUEUE_OOSEQ + /* We queue the segment on the ->ooseq queue. */ + if (pcb->ooseq == NULL) { + pcb->ooseq = tcp_seg_copy(&inseg); + } else { + /* If the queue is not empty, we walk through the queue and + try to find a place where the sequence number of the + incoming segment is between the sequence numbers of the + previous and the next segment on the ->ooseq queue. That is + the place where we put the incoming segment. If needed, we + trim the second edges of the previous and the incoming + segment so that it will fit into the sequence. + + If the incoming segment has the same sequence number as a + segment on the ->ooseq queue, we discard the segment that + contains less data. */ + + prev = NULL; + for(next = pcb->ooseq; next != NULL; next = next->next) {//ooseqȡµMĶΣñĶηǿգM++ + if (seqno == next->tcphdr->seqno) {//ñĶʼ== ҪıĶα + /* The sequence number of the incoming segment is the + same as the sequence number of the segment on + ->ooseq. We check the lengths to see which one to + discard. */ + if (inseg.len > next->len) {//ҪıĶαŸ + /* The incoming segment is larger than the old + segment. We replace some segments with the new + one. */ + cseg = tcp_seg_copy(&inseg);//ҪıĶδMĶ + if (cseg != NULL) { + if (prev != NULL) { + prev->next = cseg; + } else { + pcb->ooseq = cseg; + } + tcp_oos_insert_segment(cseg, next); + } + break; + } else { + /* Either the lenghts are the same or the incoming + segment was smaller than the old one; in either + case, we ditch the incoming segment. */ + break; + } + } else { + if (prev == NULL) { + if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) { + /* The sequence number of the incoming segment is lower + than the sequence number of the first segment on the + queue. We put the incoming segment first on the + queue. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + pcb->ooseq = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; + } + } else { + /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) && + TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/ + if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) { + /* The sequence number of the incoming segment is in + between the sequence numbers of the previous and + the next segment on ->ooseq. We trim trim the previous + segment, delete next segments that included in received segment + and trim received, if needed. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) { + /* We need to trim the prev segment. */ + prev->len = (u16_t)(seqno - prev->tcphdr->seqno); + pbuf_realloc(prev->p, prev->len); + } + prev->next = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; + } + } + /* If the "next" segment is the last segment on the + ooseq queue, we add the incoming segment to the end + of the list. */ + if (next->next == NULL && + TCP_SEQ_GT(seqno, next->tcphdr->seqno)) { + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + /* segment "next" already contains all data */ + break; + } + next->next = tcp_seg_copy(&inseg); + if (next->next != NULL) { + if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) { + /* We need to trim the last segment. */ + next->len = (u16_t)(seqno - next->tcphdr->seqno); + pbuf_realloc(next->p, next->len); + } + /* check if the remote side overruns our receive window */ + if ((u32_t)tcplen + seqno > pcb->rcv_nxt + (u32_t)pcb->rcv_wnd) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) &~ TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno; + pbuf_realloc(next->next->p, next->next->len); + tcplen = TCP_TCPLEN(next->next); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } + } + break; + } + } + prev = next; + } + } +#endif /* TCP_QUEUE_OOSEQ */ + + } + } else { + /* The incoming segment is not withing the window. */ + tcp_send_empty_ack(pcb); + } + } else { + /* Segments with length 0 is taken care of here. Segments that + fall out of the window are ACKed. */ + /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) || + TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ + if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){ + tcp_ack_now(pcb);//Դ˷һȷϱ + } + } +} + +/** + * Parses the options contained in the incoming segment. + * + * Called from tcp_listen_input() and tcp_process(). + * Currently, only the MSS option is supported! + * + * @param pcb the tcp_pcb for which a segment arrived + */ +static void +tcp_parseopt(struct tcp_pcb *pcb) +{ + u16_t c, max_c; + u16_t mss; + u8_t *opts, opt; +#if LWIP_TCP_TIMESTAMPS + u32_t tsval; +#endif + + opts = (u8_t *)tcphdr + TCP_HLEN; + + /* Parse the TCP MSS option, if present. */ + if(TCPH_HDRLEN(tcphdr) > 0x5) { + max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2; + for (c = 0; c < max_c; ) { + opt = opts[c]; + switch (opt) { + case 0x00: + /* End of options. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n")); + return; + case 0x01: + /* NOP option. */ + ++c; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n")); + break; + case 0x02: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n")); + if (opts[c + 1] != 0x04 || c + 0x04 > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* An MSS option with the right option length. */ + mss = (opts[c + 2] << 8) | opts[c + 3]; + /* Limit the mss to the configured TCP_MSS and prevent division by zero */ + pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss; + /* Advance to next option */ + c += 0x04; + break; +#if LWIP_TCP_TIMESTAMPS + case 0x08: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n")); + if (opts[c + 1] != 0x0A || c + 0x0A > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* TCP timestamp option with valid length */ + tsval = (opts[c+2]) | (opts[c+3] << 8) | + (opts[c+4] << 16) | (opts[c+5] << 24); + if (flags & TCP_SYN) { + pcb->ts_recent = ntohl(tsval); + pcb->flags |= TF_TIMESTAMP; + } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) { + pcb->ts_recent = ntohl(tsval); + } + /* Advance to next option */ + c += 0x0A; + break; +#endif + default: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n")); + if (opts[c + 1] == 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + /* If the length field is zero, the options are malformed + and we don't process them further. */ + return; + } + /* All other options have a length field, so that we easily + can skip past them. */ + c += opts[c + 1]; + } + } + } +} + +#endif /* LWIP_TCP */ diff --git a/app/lwip/core/tcp_out.c b/app/lwip/core/tcp_out.c new file mode 100644 index 00000000..9227fec5 --- /dev/null +++ b/app/lwip/core/tcp_out.c @@ -0,0 +1,1472 @@ +/** + * @file + * Transmission Control Protocol, outgoing traffic + * + * The output functions of TCP. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp_impl.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/sys.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" + +#include + +/* Define some copy-macros for checksum-on-copy so that the code looks + nicer by preventing too many ifdef's. */ +#if TCP_CHECKSUM_ON_COPY +#define TCP_DATA_COPY(dst, src, len, seg) do { \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \ + len, &seg->chksum, &seg->chksum_swapped); \ + seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped); +#else /* TCP_CHECKSUM_ON_COPY*/ +#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len) +#endif /* TCP_CHECKSUM_ON_COPY*/ + +/** Define this to 1 for an extra check that the output checksum is valid + * (usefule when the checksum is generated by the application, not the stack) */ +#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK +#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0 +#endif + +/* Forward declarations.*/ +static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); + +/** Allocate a pbuf and create a tcphdr at p->payload, used for output + * functions other than the default tcp_output -> tcp_output_segment + * (e.g. tcp_send_empty_ack, etc.) + * + * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr) + * @param optlen length of header-options + * @param datalen length of tcp data to reserve in pbuf + * @param seqno_be seqno in network byte order (big-endian) + * @return pbuf with p->payload being the tcp_hdr + */ +static struct pbuf *ICACHE_FLASH_ATTR +tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen, + u32_t seqno_be /* already in network byte order */) +{ + struct tcp_hdr *tcphdr; + struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM); + if (p != NULL) { + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= TCP_HLEN + optlen)); + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = seqno_be; + tcphdr->ackno = htonl(pcb->rcv_nxt); + TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); + tcphdr->chksum = 0; + tcphdr->urgp = 0; + + /* If we're sending a packet, update the announced right window edge */ + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + } + return p; +} + +/** + * Called by tcp_close() to send a segment including FIN flag but not data. + * + * @param pcb the tcp_pcb over which to send a segment + * @return ERR_OK if sent, another err_t otherwise + */ +err_t +tcp_send_fin(struct tcp_pcb *pcb) +{ + /* first, try to add the fin to the last unsent segment */ + if (pcb->unsent != NULL) { + struct tcp_seg *last_unsent; + for (last_unsent = pcb->unsent; last_unsent->next != NULL; + last_unsent = last_unsent->next); + + if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) { + /* no SYN/FIN/RST flag in the header, we can add the FIN flag */ + TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN); + return ERR_OK; + } + } + /* no data, no length, flags, copy=1, no optdata */ + return tcp_enqueue_flags(pcb, TCP_FIN); +} + +/** + * Create a TCP segment with prefilled header. + * + * Called by tcp_write and tcp_enqueue_flags. + * + * @param pcb Protocol control block for the TCP connection. + * @param p pbuf that is used to hold the TCP header. + * @param flags TCP flags for header. + * @param seqno TCP sequence number of this packet + * @param optflags options to include in TCP header + * @return a new tcp_seg pointing to p, or NULL. + * The TCP header is filled in except ackno and wnd. + * p is freed on failure. + */ +static struct tcp_seg *ICACHE_FLASH_ATTR +tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags) +{ + struct tcp_seg *seg; + u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags); + + if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n")); + pbuf_free(p); + return NULL; + } + seg->flags = optflags; + seg->next = NULL; + seg->p = p; + seg->len = p->tot_len - optlen; +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = 0; +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = 0; + seg->chksum_swapped = 0; + /* check optflags */ + LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED", + (optflags & TF_SEG_DATA_CHECKSUMMED) == 0); +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* build TCP header */ + if (pbuf_header(p, TCP_HLEN)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n")); + TCP_STATS_INC(tcp.err); + tcp_seg_free(seg); + return NULL; + } + seg->tcphdr = (struct tcp_hdr *)seg->p->payload; + seg->tcphdr->src = htons(pcb->local_port); + seg->tcphdr->dest = htons(pcb->remote_port); + seg->tcphdr->seqno = htonl(seqno); + /* ackno is set in tcp_output */ + TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags); + /* wnd and chksum are set in tcp_output */ + seg->tcphdr->urgp = 0; + return seg; +} + +/** + * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end. + * + * This function is like pbuf_alloc(layer, length, PBUF_RAM) except + * there may be extra bytes available at the end. + * + * @param layer flag to define header size. + * @param length size of the pbuf's payload. + * @param max_length maximum usable size of payload+oversize. + * @param oversize pointer to a u16_t that will receive the number of usable tail bytes. + * @param pcb The TCP connection that willo enqueue the pbuf. + * @param apiflags API flags given to tcp_write. + * @param first_seg true when this pbuf will be used in the first enqueued segment. + * @param + */ +#if TCP_OVERSIZE +static struct pbuf *ICACHE_FLASH_ATTR +tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length, + u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags, + u8_t first_seg) +{ + struct pbuf *p; + u16_t alloc = length; + +#if LWIP_NETIF_TX_SINGLE_PBUF + LWIP_UNUSED_ARG(max_length); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(apiflags); + LWIP_UNUSED_ARG(first_seg); + /* always create MSS-sized pbufs */ + alloc = TCP_MSS; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (length < max_length) { + /* Should we allocate an oversized pbuf, or just the minimum + * length required? If tcp_write is going to be called again + * before this segment is transmitted, we want the oversized + * buffer. If the segment will be transmitted immediately, we can + * save memory by allocating only length. We use a simple + * heuristic based on the following information: + * + * Did the user set TCP_WRITE_FLAG_MORE? + * + * Will the Nagle algorithm defer transmission of this segment? + */ + if ((apiflags & TCP_WRITE_FLAG_MORE) || + (!(pcb->flags & TF_NODELAY) && + (!first_seg || + pcb->unsent != NULL || + pcb->unacked != NULL))) { + alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE)); + } + } +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + p = pbuf_alloc(layer, alloc, PBUF_RAM); + if (p == NULL) { + return NULL; + } + LWIP_ASSERT("need unchained pbuf", p->next == NULL); + *oversize = p->len - length; + /* trim p->len to the currently used size */ + p->len = p->tot_len = length; + return p; +} +#else /* TCP_OVERSIZE */ +#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM) +#endif /* TCP_OVERSIZE */ + +#if TCP_CHECKSUM_ON_COPY +/** Add a checksum of newly added data to the segment */ +static void ICACHE_FLASH_ATTR +tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum, + u8_t *seg_chksum_swapped) +{ + u32_t helper; + /* add chksum to old chksum and fold to u16_t */ + helper = chksum + *seg_chksum; + chksum = FOLD_U32T(helper); + if ((len & 1) != 0) { + *seg_chksum_swapped = 1 - *seg_chksum_swapped; + chksum = SWAP_BYTES_IN_WORD(chksum); + } + *seg_chksum = chksum; +} +#endif /* TCP_CHECKSUM_ON_COPY */ + +/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen). + * + * @param pcb the tcp pcb to check for + * @param len length of data to send (checked agains snd_buf) + * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise + */ +static err_t ICACHE_FLASH_ATTR +tcp_write_checks(struct tcp_pcb *pcb, u16_t len) +{ + /* connection is in invalid state for data transmission? */ + if ((pcb->state != ESTABLISHED) && + (pcb->state != CLOSE_WAIT) && + (pcb->state != SYN_SENT) && + (pcb->state != SYN_RCVD)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n")); + return ERR_CONN; + } else if (len == 0) { + return ERR_OK; + } + + /* fail on too much data */ + if (len > pcb->snd_buf) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", + len, pcb->snd_buf)); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + /* If total number of pbufs on the unsent/unacked queues exceeds the + * configured maximum, return an error */ + /* check for configured max queuelen and possible overflow */ + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n", + pcb->snd_queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty", + pcb->unacked != NULL || pcb->unsent != NULL); + } else { + LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty", + pcb->unacked == NULL && pcb->unsent == NULL); + } + return ERR_OK; +} + +/** + * Write data for sending (but does not send it immediately). + *һݣúһĶβڿƿ黺 + * It waits in the expectation of more data being sent soon (as + * it can send them more efficiently by combining them together). + * To prompt the system to send data now, call tcp_output() after + * calling tcp_write(). + * + * @param pcb Protocol control block for the TCP connection to enqueue data for.Ӧӿƿ + * @param arg Pointer to the data to be enqueued for sending.ʼַ + * @param len Data length in bytesݳ + * @param apiflags combination of following flags :ǷпԼĶײǷPSH־ + * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack + * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, + * @return ERR_OK if enqueued, another err_t on error + */ +err_t +tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) +{ + struct pbuf *concat_p = NULL; + struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL; + u16_t pos = 0; /* position in 'arg' data */ + u16_t queuelen; + u8_t optlen = 0; + u8_t optflags = 0; +#if TCP_OVERSIZE + u16_t oversize = 0; + u16_t oversize_used = 0; +#endif /* TCP_OVERSIZE */ +#if TCP_CHECKSUM_ON_COPY + u16_t concat_chksum = 0; + u8_t concat_chksum_swapped = 0; + u16_t concat_chksummed = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ + err_t err; + +#if LWIP_NETIF_TX_SINGLE_PBUF + /* Always copy to try to create single pbufs for TX */ + apiflags |= TCP_WRITE_FLAG_COPY; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n", + (void *)pcb, arg, len, (u16_t)apiflags)); + LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)", + arg != NULL, return ERR_ARG;); + + err = tcp_write_checks(pcb, len); + if (err != ERR_OK) { + return err; + } + queuelen = pcb->snd_queuelen; + +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + optflags = TF_SEG_OPTS_TS; + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + } +#endif /* LWIP_TCP_TIMESTAMPS */ + + + /* + * TCP segmentation is done in three phases with increasing complexity: + * + * 1. Copy data directly into an oversized pbuf. + * 2. Chain a new pbuf to the end of pcb->unsent. + * 3. Create new segments. + * + * We may run out of memory at any point. In that case we must + * return ERR_MEM and not change anything in pcb. Therefore, all + * changes are recorded in local variables and committed at the end + * of the function. Some pcb fields are maintained in local copies: + * + * queuelen = pcb->snd_queuelen + * oversize = pcb->unsent_oversize + * + * These variables are set consistently by the phases: + * + * seg points to the last segment tampered with. + * + * pos records progress as data is segmented. + */ + + /* Find the tail of the unsent queue. */ + if (pcb->unsent != NULL) { + u16_t space; + u16_t unsent_optlen; + + /* @todo: this could be sped up by keeping last_unsent in the pcb */ + for (last_unsent = pcb->unsent; last_unsent->next != NULL; + last_unsent = last_unsent->next); + + /* Usable space at the end of the last unsent segment */ + unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags); + space = pcb->mss - (last_unsent->len + unsent_optlen); + + /* + * Phase 1: Copy data directly into an oversized pbuf. + * + * The number of bytes copied is recorded in the oversize_used + * variable. The actual copying is done at the bottom of the + * function. + */ +#if TCP_OVERSIZE +#if TCP_OVERSIZE_DBGCHECK + /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */ + LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)", + pcb->unsent_oversize == last_unsent->oversize_left); +#endif /* TCP_OVERSIZE_DBGCHECK */ + oversize = pcb->unsent_oversize; + if (oversize > 0) { + LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space); + seg = last_unsent; + oversize_used = oversize < len ? oversize : len; + pos += oversize_used; + oversize -= oversize_used; + space -= oversize_used; + } + /* now we are either finished or oversize is zero */ + LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len)); +#endif /* TCP_OVERSIZE */ + + /* + * Phase 2: Chain a new pbuf to the end of pcb->unsent. + * + * We don't extend segments containing SYN/FIN flags or options + * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at + * the end. + */ + if ((pos < len) && (space > 0) && (last_unsent->len > 0)) { + u16_t seglen = space < len - pos ? space : len - pos; + seg = last_unsent; + + /* Create a pbuf with a copy or reference to seglen bytes. We + * can use PBUF_RAW here since the data appears in the middle of + * a segment. A header will never be prepended. */ + if (apiflags & TCP_WRITE_FLAG_COPY) { + /* Data is copied */ + if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, + ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", + seglen)); + goto memerr; + } +#if TCP_OVERSIZE_DBGCHECK + last_unsent->oversize_left = oversize; +#endif /* TCP_OVERSIZE_DBGCHECK */ + TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped); +#if TCP_CHECKSUM_ON_COPY + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ + } else { + /* Data is not copied */ + if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, + ("tcp_write: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen, + &concat_chksum, &concat_chksum_swapped); + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ + /* reference the non-volatile payload data */ + concat_p->payload = (u8_t*)arg + pos; + } + + pos += seglen; + queuelen += pbuf_clen(concat_p); + } + } else { +#if TCP_OVERSIZE + LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)", + pcb->unsent_oversize == 0); +#endif /* TCP_OVERSIZE */ + } + + /* + * Phase 3: Create new segments. + * + * The new segments are chained together in the local 'queue' + * variable, ready to be appended to pcb->unsent. + */ + while (pos < len) { + struct pbuf *p; + u16_t left = len - pos; + u16_t max_len = pcb->mss - optlen; + u16_t seglen = left > max_len ? max_len : left; +#if TCP_CHECKSUM_ON_COPY + u16_t chksum = 0; + u8_t chksum_swapped = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ + + if (apiflags & TCP_WRITE_FLAG_COPY) { + /* If copy is set, memory should be allocated and data copied + * into pbuf */ + if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, pcb->mss, &oversize, pcb, apiflags, queue == NULL)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); + goto memerr; + } + LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen", + (p->len >= seglen)); + TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped); + } else { + /* Copy is not set: First allocate a pbuf for holding the data. + * Since the referenced data is available at least until it is + * sent out on the link (as it has to be ACKed by the remote + * party) we can safely use PBUF_ROM instead of PBUF_REF here. + */ + struct pbuf *p2; +#if TCP_OVERSIZE + LWIP_ASSERT("oversize == 0", oversize == 0); +#endif /* TCP_OVERSIZE */ + if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + chksum = ~inet_chksum((u8_t*)arg + pos, seglen); +#endif /* TCP_CHECKSUM_ON_COPY */ + /* reference the non-volatile payload data */ + p2->payload = (u8_t*)arg + pos; + + /* Second, allocate a pbuf for the headers. */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + /* If allocation fails, we have to deallocate the data pbuf as + * well. */ + pbuf_free(p2); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n")); + goto memerr; + } + /* Concatenate the headers and data pbufs together. */ + pbuf_cat(p/*header*/, p2/*data*/); + } + + queuelen += pbuf_clen(p); + + /* Now that there are more segments queued, we check again if the + * length of the queue exceeds the configured maximum or + * overflows. */ + if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + pbuf_free(p); + goto memerr; + } + + if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) { + goto memerr; + } +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = oversize; +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = chksum; + seg->chksum_swapped = chksum_swapped; + seg->flags |= TF_SEG_DATA_CHECKSUMMED; +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* first segment of to-be-queued data? */ + if (queue == NULL) { + queue = seg; + } else { + /* Attach the segment to the end of the queued segments */ + LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL); + prev_seg->next = seg; + } + /* remember last segment of to-be-queued data for next iteration */ + prev_seg = seg; + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg))); + + pos += seglen; + } + + /* + * All three segmentation phases were successful. We can commit the + * transaction. + */ + + /* + * Phase 1: If data has been added to the preallocated tail of + * last_unsent, we update the length fields of the pbuf chain. + */ +#if TCP_OVERSIZE + if (oversize_used > 0) { + struct pbuf *p; + /* Bump tot_len of whole chain, len of tail */ + for (p = last_unsent->p; p; p = p->next) { + p->tot_len += oversize_used; + if (p->next == NULL) { + TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent); + p->len += oversize_used; + } + } + last_unsent->len += oversize_used; +#if TCP_OVERSIZE_DBGCHECK + last_unsent->oversize_left -= oversize_used; +#endif /* TCP_OVERSIZE_DBGCHECK */ + } + pcb->unsent_oversize = oversize; +#endif /* TCP_OVERSIZE */ + + /* + * Phase 2: concat_p can be concatenated onto last_unsent->p + */ + if (concat_p != NULL) { + LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty", + (last_unsent != NULL)); + pbuf_cat(last_unsent->p, concat_p); + last_unsent->len += concat_p->tot_len; +#if TCP_CHECKSUM_ON_COPY + if (concat_chksummed) { + tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum, + &last_unsent->chksum_swapped); + last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED; + } +#endif /* TCP_CHECKSUM_ON_COPY */ + } + + /* + * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that + * is harmless + */ + if (last_unsent == NULL) { + pcb->unsent = queue; + } else { + last_unsent->next = queue; + } + + /* + * Finally update the pcb state. + */ + pcb->snd_lbb += len; + pcb->snd_buf -= len; + pcb->snd_queuelen = queuelen; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n", + pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + /* Set the PSH flag in the last segment that we enqueued. */ + if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) { + TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); + } + + return ERR_OK; +memerr: + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + + if (concat_p != NULL) { + pbuf_free(concat_p); + } + if (queue != NULL) { + tcp_segs_free(queue); + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); + return ERR_MEM; +} + +/** + * Enqueue TCP options for transmission. + * + * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl(). + * + * @param pcb Protocol control block for the TCP connection. + * @param flags TCP header flags to set in the outgoing segment. + * @param optdata pointer to TCP options, or NULL. + * @param optlen length of TCP options in bytes. + */ +err_t +tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags) +{ + struct pbuf *p; + struct tcp_seg *seg; + u8_t optflags = 0; + u8_t optlen = 0; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)", + (flags & (TCP_SYN | TCP_FIN)) != 0); + + /* check for configured max queuelen and possible overflow */ + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n", + pcb->snd_queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + + if (flags & TCP_SYN) { + optflags = TF_SEG_OPTS_MSS; + } +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + optflags |= TF_SEG_OPTS_TS; + } +#endif /* LWIP_TCP_TIMESTAMPS */ + optlen = LWIP_TCP_OPT_LENGTH(optflags); + + /* tcp_enqueue_flags is always called with either SYN or FIN in flags. + * We need one available snd_buf byte to do that. + * This means we can't send FIN while snd_buf==0. A better fix would be to + * not include SYN and FIN sequence numbers in the snd_buf count. */ + if (pcb->snd_buf == 0) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n")); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + + /* Allocate pbuf with room for TCP header + options */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen", + (p->len >= optlen)); + + /* Allocate memory for tcp_seg, and fill in fields. */ + if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) { + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0); + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, + ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), + (u16_t)flags)); + + /* Now append seg to pcb->unsent queue */ + if (pcb->unsent == NULL) { + pcb->unsent = seg; + } else { + struct tcp_seg *useg; + for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); + useg->next = seg; + } +#if TCP_OVERSIZE + /* The new unsent tail has no space */ + pcb->unsent_oversize = 0; +#endif /* TCP_OVERSIZE */ + + /* SYN and FIN bump the sequence number */ + if ((flags & TCP_SYN) || (flags & TCP_FIN)) { + pcb->snd_lbb++; + /* optlen does not influence snd_buf */ + pcb->snd_buf--; + } + if (flags & TCP_FIN) { + pcb->flags |= TF_FIN; + } + + /* update number of segments on the queues */ + pcb->snd_queuelen += pbuf_clen(seg->p); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_enqueue_flags: invalid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + return ERR_OK; +} + + +#if LWIP_TCP_TIMESTAMPS +/* Build a timestamp option (12 bytes long) at the specified options pointer) + * + * @param pcb tcp_pcb + * @param opts option pointer where to store the timestamp option + */ +static void ICACHE_FLASH_ATTR +tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts) +{ + /* Pad with two NOP options to make everything nicely aligned */ + opts[0] = PP_HTONL(0x0101080A); + opts[1] = htonl(sys_now()); + opts[2] = htonl(pcb->ts_recent); +} +#endif + +/** Send an ACK without data. + * + * @param pcb Protocol control block for the TCP connection to send the ACK + */ +err_t +tcp_send_empty_ack(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + u8_t optlen = 0; + +#if LWIP_TCP_TIMESTAMPS + if (pcb->flags & TF_TIMESTAMP) { + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + } +#endif + + p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt)); + if (p == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); + return ERR_BUF; + } + tcphdr = (struct tcp_hdr *)p->payload; + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, + ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); + /* remove ACK flags from the PCB, as we send an empty ACK now */ + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + + /* NB. MSS option is only sent on SYNs, so ignore it here */ +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (pcb->flags & TF_TIMESTAMP) { + tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1)); + } +#endif + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), + IP_PROTO_TCP, p->tot_len); +#endif +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP, &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + pbuf_free(p); + + return ERR_OK; +} + +/** + * Find out what we can send and send it + *Ϳƿ黺еıĶ + * @param pcb Protocol control block for the TCP connection to send data + * @return ERR_OK if data has been sent or nothing to send + * another err_t on error + */ +err_t ICACHE_FLASH_ATTR +tcp_output(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg, *useg; + u32_t wnd, snd_nxt; +#if TCP_CWND_DEBUG + s16_t i = 0; +#endif /* TCP_CWND_DEBUG */ + /* First, check if we are invoked by the TCP input processing + code. If so, we do not output anything. Instead, we rely on the + input processing code to call us when input processing is done + with. ƿ鵱ǰݱֱӷ*/ + if (tcp_input_pcb == pcb) { + return ERR_OK; + } + + wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);//ӷʹںȡСߵõЧʹ + + seg = pcb->unsent; + + /* If the TF_ACK_NOW flag is set and no data will be sent (either + * because the ->unsent queue is empty or because the window does + * not allow it), construct an empty ACK segment and send it. + * + * If data is to be sent, we will just piggyback the ACK (see below). + */ + if (pcb->flags & TF_ACK_NOW && + (seg == NULL || + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { + return tcp_send_empty_ack(pcb);//ֻACKıĶ + } + + /* useg should point to last segment on unacked queue */ + useg = pcb->unacked; + if (useg != NULL) { + for (; useg->next != NULL; useg = useg->next);//õβ + } + +#if TCP_OUTPUT_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", + (void*)pcb->unsent)); + } +#endif /* TCP_OUTPUT_DEBUG */ +#if TCP_CWND_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F + ", cwnd %"U16_F", wnd %"U32_F + ", seg == NULL, ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack)); + } else { + LWIP_DEBUGF(TCP_CWND_DEBUG, + ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F + ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, + ntohl(seg->tcphdr->seqno), pcb->lastack)); + } +#endif /* TCP_CWND_DEBUG */ + /* data available and window allows it to be sent? + *ǰЧķͣѭͱģֱ*/ + while (seg != NULL && + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + LWIP_ASSERT("RST not expected here!", + (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0); + /* Stop sending if the nagle algorithm would prevent it + * Don't stop: + * - if tcp_write had a memory error before (prevent delayed ACK timeout) or + * - if FIN was already enqueued for this PCB (SYN is always alone in a segment - + * either seg->next != NULL or pcb->unacked == NULL; + * RST is no sent using tcp_write/tcp_output. + */ + if((tcp_do_output_nagle(pcb) == 0) && + ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){ + break; + } +#if TCP_CWND_DEBUG + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) + seg->len - + pcb->lastack, + ntohl(seg->tcphdr->seqno), pcb->lastack, i)); + ++i; +#endif /* TCP_CWND_DEBUG */ + + pcb->unsent = seg->next; + + if (pcb->state != SYN_SENT) { + TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);//дײACK־ + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);//־λ + } + + tcp_output_segment(seg, pcb);//úͱĶ + + snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);//snd_nxt + if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { + pcb->snd_nxt = snd_nxt;//Ҫ͵ݱ + } + /* put segment on unacknowledged list if length > 0 ȥıĶݳȲΪ0ߴ + * SYNFIN־򽫸ñĶμ뵽δȷ϶УԱ㳬ʱش*/ + if (TCP_TCPLEN(seg) > 0) { + seg->next = NULL; + /* unacked list is empty? ֱӹҽ*/ + if (pcb->unacked == NULL) { + pcb->unacked = seg; + useg = seg; + /* unacked list is not empty?ǰİ˳֯ڶ */ + } else { + /* In the case of fast retransmit, the packet should not go to the tail + * of the unacked queue, but rather somewhere before it. We need to check for + * this case. -STJ Jul 27, 2004 */ //ǰĵкŵڶβкţ + //Ӷײʼ + if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) { + /* add segment to before tail of unacked list, keeping the list sorted */ + struct tcp_seg **cur_seg = &(pcb->unacked); + while (*cur_seg && + TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = (*cur_seg); + (*cur_seg) = seg; + } else {//ߣδȷ϶ĩβ + /* add segment to tail of unacked list */ + useg->next = seg; + useg = useg->next; + } + } + /* do not queue empty segments on the unacked list */ + } else {//ĶγΪ0ֱɾش + tcp_seg_free(seg); + } + seg = pcb->unsent;//һĶ + } +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + /* last unsent has been removed, reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + +//ʹ±IJܷͣ㴰̽⡣ + if (seg != NULL && pcb->persist_backoff == 0 && + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) { + /* prepare for persist timer */ + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + + pcb->flags &= ~TF_NAGLEMEMERR;//ڴ־ + return ERR_OK; +} + +/** + * Called by tcp_output() to actually send a TCP segment over IP. + * + * @param seg the tcp_seg to send + * @param pcb the tcp_pcb for the TCP connection used to send the segment + */ + +static void +tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) +{ + u16_t len; + struct netif *netif; + u32_t *opts; + /** @bug Exclude retransmitted segments from this count. */ + snmp_inc_tcpoutsegs(); + + /* The TCP header has already been constructed, but the ackno and + wnd fields remain. */ + seg->tcphdr->ackno = htonl(pcb->rcv_nxt); + + /* advertise our receive window size in this TCP segment */ + seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd); + + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + + /* Add any requested options. NB MSS option is only set on SYN + packets, so ignore it here */ + LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0); + opts = (u32_t *)(void *)(seg->tcphdr + 1); + if (seg->flags & TF_SEG_OPTS_MSS) { + TCP_BUILD_MSS_OPTION(*opts); + opts += 1; + } +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (seg->flags & TF_SEG_OPTS_TS) { + tcp_build_timestamp_option(pcb, opts); + opts += 3; + } +#endif + + /* Set retransmission timer running if it is not currently enabled + This must be set before checking the route. modify by ives at 2014.4.24*/ + if (pcb->rtime == -1) { + pcb->rtime = 0; + } + + /* If we don't have a local IP address, we get one by + calling ip_route(). */ + if (ip_addr_isany(&(pcb->local_ip))) { + netif = ip_route(&(pcb->remote_ip)); + if (netif == NULL) { + return; + } + ip_addr_copy(pcb->local_ip, netif->ip_addr); + } + + if (pcb->rttest == 0) { + pcb->rttest = tcp_ticks; + pcb->rtseq = ntohl(seg->tcphdr->seqno); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq)); + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n", + htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) + + seg->len)); + + len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); + + seg->p->len -= len; + seg->p->tot_len -= len; + + seg->p->payload = seg->tcphdr; + + seg->tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP +#if TCP_CHECKSUM_ON_COPY + { + u32_t acc; +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + u16_t chksum_slow = inet_chksum_pseudo(seg->p, &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len); +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) { + LWIP_ASSERT("data included but not checksummed", + seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4)); + } + + /* rebuild TCP header checksum (TCP header changes for retransmissions!) */ + acc = inet_chksum_pseudo_partial(seg->p, &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4); + /* add payload checksum */ + if (seg->chksum_swapped) { + seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum); + seg->chksum_swapped = 0; + } + acc += (u16_t)~(seg->chksum); + seg->tcphdr->chksum = FOLD_U32T(acc); +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + if (chksum_slow != seg->tcphdr->chksum) { + LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n", + seg->tcphdr->chksum, chksum_slow)); + seg->tcphdr->chksum = chksum_slow; + } +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + } +#else /* TCP_CHECKSUM_ON_COPY */ + seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len); +#endif /* TCP_CHECKSUM_ON_COPY */ +#endif /* CHECKSUM_GEN_TCP */ + TCP_STATS_INC(tcp.xmit); + +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP, &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ +} + +/** + * Send a TCP RESET packet (empty segment with RST flag set) either to + * abort a connection or to show that there is no matching local connection + * for a received segment. + * + * Called by tcp_abort() (to abort a local connection), tcp_input() (if no + * matching local pcb was found), tcp_listen_input() (if incoming segment + * has ACK flag set) and tcp_process() (received segment in the wrong state) + * + * Since a RST segment is in most cases not sent for an active connection, + * tcp_rst() has a number of arguments that are taken from a tcp_pcb for + * most other segment output functions. + * + * @param seqno the sequence number to use for the outgoing segment + * @param ackno the acknowledge number to use for the outgoing segment + * @param local_ip the local IP address to send the segment from + * @param remote_ip the remote IP address to send the segment to + * @param local_port the local TCP port to send the segment from + * @param remote_port the remote TCP port to send the segment to + */ +void +tcp_rst(u32_t seqno, u32_t ackno, + ip_addr_t *local_ip, ip_addr_t *remote_ip, + u16_t local_port, u16_t remote_port) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = htons(local_port); + tcphdr->dest = htons(remote_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK); + tcphdr->wnd = PP_HTONS(TCP_WND); + tcphdr->chksum = 0; + tcphdr->urgp = 0; + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip, + IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + snmp_inc_tcpoutrsts(); + /* Send output with hardcoded TTL since we have no access to the pcb */ + ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP); + pbuf_free(p); + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); +} + +/** + * Requeue all unacked segments for retransmission + * + * Called by tcp_slowtmr() for slow retransmission. + * + * @param pcb the tcp_pcb for which to re-enqueue all unacked segments + */ +void +tcp_rexmit_rto(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move all unacked segments to the head of the unsent queue */ + for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); + /* concatenate unsent queue after unacked queue */ + seg->next = pcb->unsent; + /* unsent queue is the concatenated queue (of unacked, unsent) */ + pcb->unsent = pcb->unacked; + /* unacked queue is now empty */ + pcb->unacked = NULL; + + /* increment number of retransmissions */ + ++pcb->nrtx; + + /* Don't take any RTT measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission */ + tcp_output(pcb); +} + +/** + * Requeue the first unacked segment for retransmission + * + * Called by tcp_receive() for fast retramsmit. + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + struct tcp_seg **cur_seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move the first unacked segment to the unsent queue */ + /* Keep the unsent queue sorted. */ + seg = pcb->unacked; + pcb->unacked = seg->next; + + cur_seg = &(pcb->unsent); + while (*cur_seg && + TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = *cur_seg; + *cur_seg = seg; + + ++pcb->nrtx; + + /* Don't take any rtt measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission. */ + snmp_inc_tcpretranssegs(); + /* No need to call tcp_output: we are always called from tcp_input() + and thus tcp_output directly returns. */ +} + + +/** + * Handle retransmission after three dupacks received + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit_fast(struct tcp_pcb *pcb) +{ + if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) { + /* This is fast retransmit. Retransmit the first unacked segment. */ + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: dupacks %"U16_F" (%"U32_F + "), fast retransmit %"U32_F"\n", + (u16_t)pcb->dupacks, pcb->lastack, + ntohl(pcb->unacked->tcphdr->seqno))); + tcp_rexmit(pcb); + + /* Set ssthresh to half of the minimum of the current + * cwnd and the advertised window */ + if (pcb->cwnd > pcb->snd_wnd) { + pcb->ssthresh = pcb->snd_wnd / 2; + } else { + pcb->ssthresh = pcb->cwnd / 2; + } + + /* The minimum value for ssthresh should be 2 MSS */ + if (pcb->ssthresh < 2*pcb->mss) { + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: The minimum value for ssthresh %"U16_F + " should be min 2 mss %"U16_F"...\n", + pcb->ssthresh, 2*pcb->mss)); + pcb->ssthresh = 2*pcb->mss; + } + + pcb->cwnd = pcb->ssthresh + 3 * pcb->mss; + pcb->flags |= TF_INFR; + } +} + + +/** + * Send keepalive packets to keep a connection active although + * no data is sent over it. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a keepalive packet + */ +void +tcp_keepalive(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), + ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip))); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1)); + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_keepalive: could not allocate memory for pbuf\n")); + return; + } + tcphdr = (struct tcp_hdr *)p->payload; + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, + IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, + &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} + + +/** + * Send persist timer zero-window probes to keep a connection active + * when a window update is lost. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a zero-window probe packet + */ +void +tcp_zero_window_probe(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct tcp_seg *seg; + u16_t len; + u8_t is_fin; + + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: sending ZERO WINDOW probe to %" + U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), + ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip))); + + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: tcp_ticks %"U32_F + " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + seg = pcb->unacked; + + if(seg == NULL) { + seg = pcb->unsent; + } + if(seg == NULL) { + return; + } + + is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0); + /* we want to send one seqno: either FIN or data (no options) */ + len = is_fin ? 0 : 1; + + p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno); + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); + return; + } + tcphdr = (struct tcp_hdr *)p->payload; + + if (is_fin) { + /* FIN segment, no data */ + TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN); + } else { + /* Data segment, copy in one byte from the head of the unacked queue */ + struct tcp_hdr *thdr = (struct tcp_hdr *)seg->p->payload; + char *d = ((char *)p->payload + TCP_HLEN); + pbuf_copy_partial(seg->p, d, 1, TCPH_HDRLEN(thdr) * 4); + } + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, + IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, + &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F + " ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} +#endif /* LWIP_TCP */ diff --git a/app/lwip/core/timers.c b/app/lwip/core/timers.c new file mode 100644 index 00000000..b240024a --- /dev/null +++ b/app/lwip/core/timers.c @@ -0,0 +1,509 @@ +/** + * @file + * Stack-internal timers implementation. + * This file includes timer callbacks for stack-internal timers as well as + * functions to set up or stop timers and check for expired timers. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#include "lwip/timers.h" +#include "lwip/tcp_impl.h" + +#if LWIP_TIMERS + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/tcpip.h" + +#include "lwip/ip_frag.h" +#include "netif/etharp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" + + +/** The one and only timeout list */ +static struct sys_timeo *next_timeout = NULL; +#if NO_SYS +static u32_t timeouts_last_time; +#endif /* NO_SYS */ + +#if LWIP_TCP +/** global variable that shows if the tcp timer is currently scheduled or not */ +static int tcpip_tcp_timer_active; + +/** + * Timer callback function that calls tcp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void ICACHE_FLASH_ATTR +tcpip_tcp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + /* call TCP timer handler */ + tcp_tmr(); + /* timer still needed? */ + if (tcp_active_pcbs || tcp_tw_pcbs) { + /* restart timer */ + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } else { + /* disable timer */ + tcpip_tcp_timer_active = 0; + } +} + +/** + * Called from TCP_REG when registering a new PCB: + * the reason is to have the TCP timer only running when + * there are active (or time-wait) PCBs. + */ +void +tcp_timer_needed(void) +{ + /* timer is off but needed again? */ + if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { + /* enable and start timer */ + tcpip_tcp_timer_active = 1; + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } +} + +/** + * Timer callback function that calls tcp_tmr() and reschedules itself. + * + * @param arg unused argument + */ + +static void ICACHE_FLASH_ATTR +tcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: tcp_tmr()\n")); + tcp_tmr(); + sys_timeout(TCP_TMR_INTERVAL, tcp_timer_coarse, NULL); +} + +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +/** + * Timer callback function that calls ip_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void ICACHE_FLASH_ATTR +ip_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n")); + ip_reass_tmr(); + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +} +#endif /* IP_REASSEMBLY */ + +#if LWIP_ARP +/** + * Timer callback function that calls etharp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void ICACHE_FLASH_ATTR +arp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n")); + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} +#endif /* LWIP_ARP */ + +#if LWIP_DHCP +/** + * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself. + * + * @param arg unused argument + */ +extern void dhcps_coarse_tmr(void); +static void +dhcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); + dhcp_coarse_tmr(); + dhcps_coarse_tmr(); + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); +} + +/** + * Timer callback function that calls dhcp_fine_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_fine(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n")); + dhcp_fine_tmr(); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +} +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP +/** + * Timer callback function that calls autoip_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +autoip_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n")); + autoip_tmr(); + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +} +#endif /* LWIP_AUTOIP */ + +#if LWIP_IGMP +/** + * Timer callback function that calls igmp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +igmp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n")); + igmp_tmr(); + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Timer callback function that calls dns_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dns_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n")); + dns_tmr(); + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +} +#endif /* LWIP_DNS */ + +/** Initialize this module */ +void sys_timeouts_init(void) +{ +#if IP_REASSEMBLY + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +#endif /* IP_REASSEMBLY */ +#if LWIP_ARP + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +#endif /* LWIP_ARP */ +#if LWIP_DHCP + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +#endif /* LWIP_DNS */ + +#if LWIP_TCP + //sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + sys_timeout(TCP_TMR_INTERVAL, tcp_timer_coarse, NULL); +#endif + +#if NO_SYS + /* Initialise timestamp for sys_check_timeouts */ + timeouts_last_time = NOW(); +#endif +} + +/** + * Create a one-shot timer (aka timeout). Timeouts are processed in the + * following cases: + * - while waiting for a message using sys_timeouts_mbox_fetch() + * - by calling sys_check_timeouts() (NO_SYS==1 only) + * + * @param msecs time in milliseconds after that the timer should expire + * @param handler callback function to call when msecs have elapsed + * @param arg argument to pass to the callback function + */ +#if LWIP_DEBUG_TIMERNAMES +void +sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) +#else /* LWIP_DEBUG_TIMERNAMES */ +void +sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) +#endif /* LWIP_DEBUG_TIMERNAMES */ +{ + struct sys_timeo *timeout, *t; + + timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT); + if (timeout == NULL) { + LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL); + return; + } + timeout->next = NULL; + timeout->h = handler; + timeout->arg = arg; + timeout->time = msecs; +#if LWIP_DEBUG_TIMERNAMES + timeout->handler_name = handler_name; + LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n", + (void *)timeout, msecs, handler_name, (void *)arg)); +#endif /* LWIP_DEBUG_TIMERNAMES */ + + if (next_timeout == NULL) { + next_timeout = timeout; + return; + } + + if (next_timeout->time > msecs) { + next_timeout->time -= msecs; + timeout->next = next_timeout; + next_timeout = timeout; + } else { + for(t = next_timeout; t != NULL; t = t->next) { + timeout->time -= t->time; + if (t->next == NULL || t->next->time > timeout->time) { + if (t->next != NULL) { + t->next->time -= timeout->time; + } + timeout->next = t->next; + t->next = timeout; + break; + } + } + } +} + +/** + * Go through timeout list (for this task only) and remove the first matching + * entry, even though the timeout has not triggered yet. + * + * @note This function only works as expected if there is only one timeout + * calling 'handler' in the list of timeouts. + * + * @param handler callback function that would be called by the timeout + * @param arg callback argument that would be passed to handler +*/ +void +sys_untimeout(sys_timeout_handler handler, void *arg) +{ + struct sys_timeo *prev_t, *t; + + if (next_timeout == NULL) { + return; + } + + for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { + if ((t->h == handler) && (t->arg == arg)) { + /* We have a match */ + /* Unlink from previous in list */ + if (prev_t == NULL) { + next_timeout = t->next; + } else { + prev_t->next = t->next; + } + /* If not the last one, add time of this one back to next */ + if (t->next != NULL) { + t->next->time += t->time; + } + memp_free(MEMP_SYS_TIMEOUT, t); + return; + } + } + return; +} + +#if NO_SYS +extern uint8 timer2_ms_flag; +/** Handle timeouts for NO_SYS==1 (i.e. without using + * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout + * handler functions when timeouts expire. + * + * Must be called periodically from your main loop. + */ +void +sys_check_timeouts(void) +{ + struct sys_timeo *tmptimeout; + u32_t diff; + sys_timeout_handler handler; + void *arg; + int had_one; + u32_t now; + + now = NOW(); + if (next_timeout) { + /* this cares for wraparounds */ + if (timer2_ms_flag == 0) { + diff = LWIP_U32_DIFF(now, timeouts_last_time)/((CPU_CLK_FREQ>>4)/1000); + } else { + diff = LWIP_U32_DIFF(now, timeouts_last_time)/((CPU_CLK_FREQ>>8)/1000); + } + do + { + had_one = 0; + tmptimeout = next_timeout; + if (tmptimeout->time <= diff) { + /* timeout has expired */ + had_one = 1; + timeouts_last_time = now; + diff -= tmptimeout->time; + next_timeout = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { + handler(arg); + } + } + /* repeat until all expired timers have been called */ + }while(had_one); + } +} + +/** Set back the timestamp of the last call to sys_check_timeouts() + * This is necessary if sys_check_timeouts() hasn't been called for a long + * time (e.g. while saving energy) to prevent all timer functions of that + * period being called. + */ +void +sys_restart_timeouts(void) +{ + timeouts_last_time = NOW(); +} + +#else /* NO_SYS */ + +/** + * Wait (forever) for a message to arrive in an mbox. + * While waiting, timeouts are processed. + * + * @param mbox the mbox to fetch the message from + * @param msg the place to store the message + */ +void +sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg) +{ + u32_t time_needed; + struct sys_timeo *tmptimeout; + sys_timeout_handler handler; + void *arg; + + again: + if (!next_timeout) { + time_needed = sys_arch_mbox_fetch(mbox, msg, 0); + } else { + if (next_timeout->time > 0) { + time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time); + } else { + time_needed = SYS_ARCH_TIMEOUT; + } + + if (time_needed == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = next_timeout; + next_timeout = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { + /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the + timeout handler function. */ + LOCK_TCPIP_CORE(); + handler(arg); + UNLOCK_TCPIP_CORE(); + } + LWIP_TCPIP_THREAD_ALIVE(); + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time_needed < next_timeout->time) { + next_timeout->time -= time_needed; + } else { + next_timeout->time = 0; + } + } + } +} + +#endif /* NO_SYS */ + +#else /* LWIP_TIMERS */ +/* Satisfy the TCP code which calls this function */ +void +tcp_timer_needed(void) +{ +} +#endif /* LWIP_TIMERS */ diff --git a/app/lwip/core/udp.c b/app/lwip/core/udp.c new file mode 100644 index 00000000..83386cc0 --- /dev/null +++ b/app/lwip/core/udp.c @@ -0,0 +1,964 @@ +/** + * @file + * User Datagram Protocol module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +/* udp.c + * + * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828). + * + */ + +/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'! + */ + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" +#include "lwip/dhcp.h" + +#include + +/* The list of UDP PCBs */ +/* exported in udp.h (was static) */ +struct udp_pcb *udp_pcbs; + +/** + * Process an incoming UDP datagram. + * + * Given an incoming UDP datagram (as a chain of pbufs) this function + * finds a corresponding UDP PCB and hands over the pbuf to the pcbs + * recv function. If no pcb is found or the datagram is incorrect, the + * pbuf is freed. + * + * @param p pbuf to be demultiplexed to a UDP PCB. + * @param inp network interface on which the datagram was received. + * + */ +void ICACHE_FLASH_ATTR +udp_input(struct pbuf *p, struct netif *inp) +{ + struct udp_hdr *udphdr; + struct udp_pcb *pcb, *prev; + struct udp_pcb *uncon_pcb; + struct ip_hdr *iphdr; + u16_t src, dest; + u8_t local_match; + u8_t broadcast; + + PERF_START; + + UDP_STATS_INC(udp.recv); + + iphdr = (struct ip_hdr *)p->payload; + + /* Check minimum length (IP header + UDP header) + * and move payload pointer to UDP header */ + if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4))) { + /* drop short packets */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); + UDP_STATS_INC(udp.lenerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + + udphdr = (struct udp_hdr *)p->payload; + + /* is broadcast packet ? */ + broadcast = ip_addr_isbroadcast(¤t_iphdr_dest, inp); + + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); + + /* convert src and dest ports to host byte order */ + src = ntohs(udphdr->src); + dest = ntohs(udphdr->dest); + + udp_debug_print(udphdr); + + /* print the UDP source and destination */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- " + "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", + ip4_addr1_16(&iphdr->dest), ip4_addr2_16(&iphdr->dest), + ip4_addr3_16(&iphdr->dest), ip4_addr4_16(&iphdr->dest), ntohs(udphdr->dest), + ip4_addr1_16(&iphdr->src), ip4_addr2_16(&iphdr->src), + ip4_addr3_16(&iphdr->src), ip4_addr4_16(&iphdr->src), ntohs(udphdr->src))); + +#if LWIP_DHCP + pcb = NULL; + /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by + the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */ + if (dest == DHCP_CLIENT_PORT) { + /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */ + if (src == DHCP_SERVER_PORT) { + if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) { + /* accept the packe if + (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY! + - inp->dhcp->pcb->remote == ANY or iphdr->src */ + if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) || + ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), ¤t_iphdr_src))) { + pcb = inp->dhcp->pcb; + } + } + } + } else +#endif /* LWIP_DHCP */ + { + prev = NULL; + local_match = 0; + uncon_pcb = NULL; + /* Iterate through the UDP pcb list for a matching pcb. + * 'Perfect match' pcbs (connected to the remote port & ip address) are + * preferred. If no perfect match is found, the first unconnected pcb that + * matches the local port and ip address gets the datagram. */ + for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + local_match = 0; + /* print the PCB local and remote address */ + LWIP_DEBUGF(UDP_DEBUG, + ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- " + "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", + ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), + ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), pcb->local_port, + ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), + ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip), pcb->remote_port)); + + /* compare PCB local addr+port to UDP destination addr+port */ + if ((pcb->local_port == dest) && + ((!broadcast && ip_addr_isany(&pcb->local_ip)) || + ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest) || +#if LWIP_IGMP + ip_addr_ismulticast(¤t_iphdr_dest) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && (pcb->so_options & SOF_BROADCAST)))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast))) { +#endif /* IP_SOF_BROADCAST_RECV */ + local_match = 1; + if ((uncon_pcb == NULL) && + ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { + /* the first unconnected matching PCB */ + uncon_pcb = pcb; + } + } + /* compare PCB remote addr+port to UDP source addr+port */ + if ((local_match != 0) && + (pcb->remote_port == src) && + (ip_addr_isany(&pcb->remote_ip) || + ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src))) { + /* the first fully matching PCB */ + if (prev != NULL) { + /* move the pcb to the front of udp_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } else { + UDP_STATS_INC(udp.cachehit); + } + break; + } + prev = pcb; + } + /* no fully matching pcb found? then look for an unconnected pcb */ + if (pcb == NULL) { + pcb = uncon_pcb; + } + } + + /* Check checksum if this is a match or if it was directed at us. */ + if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, ¤t_iphdr_dest)) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); +#if LWIP_UDPLITE + if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) { + /* Do the UDP Lite checksum */ +#if CHECKSUM_CHECK_UDP + u16_t chklen = ntohs(udphdr->len); + if (chklen < sizeof(struct udp_hdr)) { + if (chklen == 0) { + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet (See RFC 3828 chap. 3.1) */ + chklen = p->tot_len; + } else { + /* At least the UDP-Lite header must be covered by the + checksum! (Again, see RFC 3828 chap. 3.1) */ + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + } + if (inet_chksum_pseudo_partial(p, ¤t_iphdr_src, ¤t_iphdr_dest, + IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_input: UDP Lite datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } +#endif /* CHECKSUM_CHECK_UDP */ + } else +#endif /* LWIP_UDPLITE */ + { +#if CHECKSUM_CHECK_UDP + if (udphdr->chksum != 0) { + if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(), + IP_PROTO_UDP, p->tot_len) != 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_input: UDP datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + } +#endif /* CHECKSUM_CHECK_UDP */ + } + if(pbuf_header(p, -UDP_HLEN)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + if (pcb != NULL) { + snmp_inc_udpindatagrams(); +#if SO_REUSE && SO_REUSE_RXTOALL + if ((broadcast || ip_addr_ismulticast(¤t_iphdr_dest)) && + ((pcb->so_options & SOF_REUSEADDR) != 0)) { + /* pass broadcast- or multicast packets to all multicast pcbs + if SOF_REUSEADDR is set on the first match */ + struct udp_pcb *mpcb; + u8_t p_header_changed = 0; + for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) { + if (mpcb != pcb) { + /* compare PCB local addr+port to UDP destination addr+port */ + if ((mpcb->local_port == dest) && + ((!broadcast && ip_addr_isany(&mpcb->local_ip)) || + ip_addr_cmp(&(mpcb->local_ip), ¤t_iphdr_dest) || +#if LWIP_IGMP + ip_addr_ismulticast(¤t_iphdr_dest) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && (mpcb->so_options & SOF_BROADCAST)))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast))) { +#endif /* IP_SOF_BROADCAST_RECV */ + /* pass a copy of the packet to all local matches */ + if (mpcb->recv != NULL) { + struct pbuf *q; + /* for that, move payload to IP header again */ + if (p_header_changed == 0) { + pbuf_header(p, (s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); + p_header_changed = 1; + } + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (q != NULL) { + err_t err = pbuf_copy(q, p); + if (err == ERR_OK) { + /* move payload to UDP data */ + pbuf_header(q, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); + mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); + } + } + } + } + } + } + if (p_header_changed) { + /* and move payload to UDP data again */ + pbuf_header(p, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); + } + } +#endif /* SO_REUSE && SO_REUSE_RXTOALL */ + /* callback */ + if (pcb->recv != NULL) { + /* now the recv function is responsible for freeing p */ + pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); + } else { + /* no recv function registered? then we have to free the pbuf! */ + pbuf_free(p); + goto end; + } + } else { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); + +#if LWIP_ICMP + /* No match was found, send ICMP destination port unreachable unless + destination address was broadcast/multicast. */ + if (!broadcast && + !ip_addr_ismulticast(¤t_iphdr_dest)) { + /* move payload pointer back to ip header */ + pbuf_header(p, (IPH_HL(iphdr) * 4) + UDP_HLEN); + LWIP_ASSERT("p->payload == iphdr", (p->payload == iphdr)); + icmp_dest_unreach(p, ICMP_DUR_PORT); + } +#endif /* LWIP_ICMP */ + UDP_STATS_INC(udp.proterr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpnoports(); + pbuf_free(p); + } + } else { + pbuf_free(p); + } +end: + PERF_STOP("udp_input"); +} + +/** + * Send data using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * + * The datagram will be sent to the current remote_ip & remote_port + * stored in pcb. If the pcb is not bound to a port, it will + * automatically be bound to a random port. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_MEM. Out of memory. + * - ERR_RTE. Could not find route to destination address. + * - More errors could be returned by lower protocol layers. + * + * @see udp_disconnect() udp_sendto() + */ +err_t ICACHE_FLASH_ATTR +udp_send(struct udp_pcb *pcb, struct pbuf *p) +{ + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port); +} + +#if LWIP_CHECKSUM_ON_COPY +/** Same as udp_send() but with checksum + */ +err_t ICACHE_FLASH_ATTR +udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum) +{ + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto_chksum(pcb, p, &pcb->remote_ip, pcb->remote_port, + have_chksum, chksum); +} +#endif /* LWIP_CHECKSUM_ON_COPY */ + +/** + * Send data to a specified address using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * If the PCB already has a remote address association, it will + * be restored after the data is sent. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t ICACHE_FLASH_ATTR +udp_sendto(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port) +{ +#if LWIP_CHECKSUM_ON_COPY + return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0); +} + +/** Same as udp_sendto(), but with checksum */ +err_t ICACHE_FLASH_ATTR +udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, + u16_t dst_port, u8_t have_chksum, u16_t chksum) +{ +#endif /* LWIP_CHECKSUM_ON_COPY */ + struct netif *netif; + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); + + /* find the outgoing network interface for this packet */ +#if LWIP_IGMP + netif = ip_route((ip_addr_ismulticast(dst_ip))?(&(pcb->multicast_ip)):(dst_ip)); +#else + netif = ip_route(dst_ip); +#endif /* LWIP_IGMP */ + + /* no outgoing network interface could be found? */ + if (netif == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dst_ip), ip4_addr2_16(dst_ip), ip4_addr3_16(dst_ip), ip4_addr4_16(dst_ip))); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } +#if LWIP_CHECKSUM_ON_COPY + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum); +#else /* LWIP_CHECKSUM_ON_COPY */ + return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); +#endif /* LWIP_CHECKSUM_ON_COPY */ +} + +/** + * Send data to a specified address using UDP. + * The netif used for sending can be specified. + * + * This function exists mainly for DHCP, to be able to send UDP packets + * on a netif that is still down. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * @param netif the netif used for sending. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t ICACHE_FLASH_ATTR +udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif) +{ +#if LWIP_CHECKSUM_ON_COPY + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0); +} + +/** Same as udp_sendto_if(), but with checksum */ +err_t ICACHE_FLASH_ATTR +udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, + u16_t dst_port, struct netif *netif, u8_t have_chksum, + u16_t chksum) +{ +#endif /* LWIP_CHECKSUM_ON_COPY */ + struct udp_hdr *udphdr; + ip_addr_t *src_ip; + err_t err; + struct pbuf *q; /* q will be sent down the stack */ + +#if IP_SOF_BROADCAST + /* broadcast filter? */ + if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(dst_ip, netif) ) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + return ERR_VAL; + } +#endif /* IP_SOF_BROADCAST */ + + /* if the PCB is not yet bound to a port, bind it here */ + if (pcb->local_port == 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n")); + err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + if (err != ERR_OK) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n")); + return err; + } + } + + /* not enough space to add an UDP header to first pbuf in given p chain? */ + if (pbuf_header(p, UDP_HLEN)) { + /* allocate header in a separate new pbuf */ + q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n")); + return ERR_MEM; + } + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p (only if p contains data) */ + pbuf_chain(q, p); + } + /* first pbuf q points to header pbuf */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* adding space for header within p succeeded */ + /* first pbuf q equals given pbuf */ + q = p; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p)); + } + LWIP_ASSERT("check that first pbuf can hold struct udp_hdr", + (q->len >= sizeof(struct udp_hdr))); + /* q now represents the packet to be sent */ + udphdr = (struct udp_hdr *)q->payload; + udphdr->src = htons(pcb->local_port); + udphdr->dest = htons(dst_port); + /* in UDP, 0 checksum means 'no checksum' */ + udphdr->chksum = 0x0000; + + /* Multicast Loop? */ +#if LWIP_IGMP + if (ip_addr_ismulticast(dst_ip) && ((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0)) { + q->flags |= PBUF_FLAG_MCASTLOOP; + } +#endif /* LWIP_IGMP */ + + + /* PCB local address is IP_ANY_ADDR? */ + if (ip_addr_isany(&pcb->local_ip)) { + /* use outgoing network interface IP address as source address */ + src_ip = &(netif->ip_addr); + } else { + /* check if UDP PCB local IP address is correct + * this could be an old address if netif->ip_addr has changed */ + if (!ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) { + /* local_ip doesn't match, drop the packet */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + return ERR_VAL; + } + /* use UDP PCB local IP address as source address */ + src_ip = &(pcb->local_ip); + } + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); + +#if LWIP_UDPLITE + /* UDP Lite protocol? */ + if (pcb->flags & UDP_FLAGS_UDPLITE) { + u16_t chklen, chklen_hdr; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len)); + /* set UDP message length in UDP header */ + chklen_hdr = chklen = pcb->chksum_len_tx; + if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) { + if (chklen != 0) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen)); + } + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet. (See RFC 3828 chap. 3.1) + At least the UDP-Lite header must be covered by the + checksum, therefore, if chksum_len has an illegal + value, we generate the checksum over the complete + packet to be safe. */ + chklen_hdr = 0; + chklen = q->tot_len; + } + udphdr->len = htons(chklen_hdr); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, + IP_PROTO_UDPLITE, q->tot_len, +#if !LWIP_CHECKSUM_ON_COPY + chklen); +#else /* !LWIP_CHECKSUM_ON_COPY */ + (have_chksum ? UDP_HLEN : chklen)); + if (have_chksum) { + u32_t acc; + acc = udphdr->chksum + (u16_t)~(chksum); + udphdr->chksum = FOLD_U32T(acc); + } +#endif /* !LWIP_CHECKSUM_ON_COPY */ + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udphdr->chksum == 0x0000) { + udphdr->chksum = 0xffff; + } +#endif /* CHECKSUM_GEN_UDP */ + /* output to IP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n")); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = &(pcb->addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT*/ + err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ + } else +#endif /* LWIP_UDPLITE */ + { /* UDP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len)); + udphdr->len = htons(q->tot_len); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { + u16_t udpchksum; +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + u32_t acc; + udpchksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, IP_PROTO_UDP, + q->tot_len, UDP_HLEN); + acc = udpchksum + (u16_t)~(chksum); + udpchksum = FOLD_U32T(acc); + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + { + udpchksum = inet_chksum_pseudo(q, src_ip, dst_ip, IP_PROTO_UDP, q->tot_len); + } + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udpchksum == 0x0000) { + udpchksum = 0xffff; + } + udphdr->chksum = udpchksum; + } +#endif /* CHECKSUM_GEN_UDP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n")); + /* output to IP */ +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = &(pcb->addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT*/ + err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ + } + /* TODO: must this be increased even if error occured? */ + snmp_inc_udpoutdatagrams(); + + /* did we chain a separate header pbuf earlier? */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + + UDP_STATS_INC(udp.xmit); + return err; +} + +/** + * Bind an UDP PCB. + * + * @param pcb UDP PCB to be bound with a local address ipaddr and port. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * @param port local UDP port to bind with. Use 0 to automatically bind + * to a random port between UDP_LOCAL_PORT_RANGE_START and + * UDP_LOCAL_PORT_RANGE_END. + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified ipaddr and port are already bound to by + * another UDP PCB. + * + * @see udp_disconnect() + */ +err_t ICACHE_FLASH_ATTR +udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + u8_t rebind; + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = ")); + ip_addr_debug_print(UDP_DEBUG, ipaddr); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port)); + + rebind = 0; + /* Check for double bind and rebind of the same pcb */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + /* is this UDP PCB already on active list? */ + if (pcb == ipcb) { + /* pcb may occur at most once in active list */ + LWIP_ASSERT("rebind == 0", rebind == 0); + /* pcb already in list, just rebind */ + rebind = 1; + } + + /* By default, we don't allow to bind to a port that any other udp + PCB is alread bound to, unless *all* PCBs with that port have tha + REUSEADDR flag set. */ +#if SO_REUSE + else if (((pcb->so_options & SOF_REUSEADDR) == 0) && + ((ipcb->so_options & SOF_REUSEADDR) == 0)) { +#else /* SO_REUSE */ + /* port matches that of PCB in list and REUSEADDR not set -> reject */ + else { +#endif /* SO_REUSE */ + if ((ipcb->local_port == port) && + /* IP address matches, or one is IP_ADDR_ANY? */ + (ip_addr_isany(&(ipcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(ipcb->local_ip), ipaddr))) { + /* other PCB already binds to this local IP and port */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); + return ERR_USE; + } + } + } + + ip_addr_set(&pcb->local_ip, ipaddr); + + /* no port specified? */ + if (port == 0) { +#ifndef UDP_LOCAL_PORT_RANGE_START +#define UDP_LOCAL_PORT_RANGE_START 4096 +#define UDP_LOCAL_PORT_RANGE_END 0x7fff +#endif + port = UDP_LOCAL_PORT_RANGE_START; + ipcb = udp_pcbs; + while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) { + if (ipcb->local_port == port) { + /* port is already used by another udp_pcb */ + port++; + /* restart scanning all udp pcbs */ + ipcb = udp_pcbs; + } else { + /* go on with next udp pcb */ + ipcb = ipcb->next; + } + } + if (ipcb != NULL) { + /* no more ports available in local range */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n")); + return ERR_USE; + } + } + pcb->local_port = port; + snmp_insert_udpidx_tree(pcb); + /* pcb not active yet? */ + if (rebind == 0) { + /* place the PCB on the active list if not already there */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n", + ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), + ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), + pcb->local_port)); + return ERR_OK; +} +/** + * Connect an UDP PCB. + * + * This will associate the UDP PCB with the remote address. + * + * @param pcb UDP PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * @param port remote UDP port to connect with. + * + * @return lwIP error code + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * The udp pcb is bound to a random local port if not already bound. + * + * @see udp_disconnect() + */ +err_t ICACHE_FLASH_ATTR +udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + + if (pcb->local_port == 0) { + err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + if (err != ERR_OK) { + return err; + } + } + + ip_addr_set(&pcb->remote_ip, ipaddr); + pcb->remote_port = port; + pcb->flags |= UDP_FLAGS_CONNECTED; +/** TODO: this functionality belongs in upper layers */ +#ifdef LWIP_UDP_TODO + /* Nail down local IP for netconn_addr()/getsockname() */ + if (ip_addr_isany(&pcb->local_ip) && !ip_addr_isany(&pcb->remote_ip)) { + struct netif *netif; + + if ((netif = ip_route(&(pcb->remote_ip))) == NULL) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", pcb->remote_ip.addr)); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } + /** TODO: this will bind the udp pcb locally, to the interface which + is used to route output packets to the remote address. However, we + might want to accept incoming packets on any interface! */ + pcb->local_ip = netif->ip_addr; + } else if (ip_addr_isany(&pcb->remote_ip)) { + pcb->local_ip.addr = 0; + } +#endif + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F"\n", + ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), + ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), + pcb->local_port)); + + /* Insert UDP PCB into the list of active UDP PCBs. */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + if (pcb == ipcb) { + /* already on the list, just return */ + return ERR_OK; + } + } + /* PCB not yet on the list, add PCB now */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + return ERR_OK; +} + +/** + * Disconnect a UDP PCB + * + * @param pcb the udp pcb to disconnect. + */ +void ICACHE_FLASH_ATTR +udp_disconnect(struct udp_pcb *pcb) +{ + /* reset remote address association */ + ip_addr_set_any(&pcb->remote_ip); + pcb->remote_port = 0; + /* mark PCB as unconnected */ + pcb->flags &= ~UDP_FLAGS_CONNECTED; +} + +/** + * Set a receive callback for a UDP PCB + * + * This callback will be called when receiving a datagram for the pcb. + * + * @param pcb the pcb for wich to set the recv callback + * @param recv function pointer of the callback function + * @param recv_arg additional argument to pass to the callback function + */ +void ICACHE_FLASH_ATTR +udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv = recv; + pcb->recv_arg = recv_arg; +} + +/** + * Remove an UDP PCB. + * + * @param pcb UDP PCB to be removed. The PCB is removed from the list of + * UDP PCB's and the data structure is freed from memory. + * + * @see udp_new() + */ +void ICACHE_FLASH_ATTR +udp_remove(struct udp_pcb *pcb) +{ + struct udp_pcb *pcb2; + + snmp_delete_udpidx_tree(pcb); + /* pcb to be removed is first in list? */ + if (udp_pcbs == pcb) { + /* make list start at 2nd pcb */ + udp_pcbs = udp_pcbs->next; + /* pcb not 1st in list */ + } else { + for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in udp_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + } + memp_free(MEMP_UDP_PCB, pcb); +} + +/** + * Create a UDP PCB. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * ICACHE_FLASH_ATTR +udp_new(void) +{ + struct udp_pcb *pcb; + pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB); + /* could allocate UDP PCB? */ + if (pcb != NULL) { + /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0 + * which means checksum is generated over the whole datagram per default + * (recommended as default by RFC 3828). */ + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct udp_pcb)); + pcb->ttl = UDP_TTL; + } + return pcb; +} + +#if UDP_DEBUG +/** + * Print UDP header information for debug purposes. + * + * @param udphdr pointer to the udp header in memory. + */ +void ICACHE_FLASH_ATTR +udp_debug_print(struct udp_hdr *udphdr) +{ + LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n")); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(udphdr->src), ntohs(udphdr->dest))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n", + ntohs(udphdr->len), ntohs(udphdr->chksum))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* UDP_DEBUG */ + +#endif /* LWIP_UDP */ diff --git a/app/lwip/netif/Makefile b/app/lwip/netif/Makefile new file mode 100644 index 00000000..9e5e810a --- /dev/null +++ b/app/lwip/netif/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = liblwipnetif.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/lwip/netif/etharp.c b/app/lwip/netif/etharp.c new file mode 100644 index 00000000..efc0979d --- /dev/null +++ b/app/lwip/netif/etharp.c @@ -0,0 +1,1357 @@ +/** + * @file + * Address Resolution Protocol module for IP over Ethernet + * + * Functionally, ARP is divided into two parts. The first maps an IP address + * to a physical address when sending a packet, and the second part answers + * requests from other machines for our physical address. + * + * This implementation complies with RFC 826 (Ethernet ARP). It supports + * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6 + * if an interface calls etharp_gratuitous(our_netif) upon address change. + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET + +#include "lwip/ip_addr.h" +#include "lwip/def.h" +#include "lwip/ip.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" + +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#endif /* PPPOE_SUPPORT */ + +#include + +const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +const struct eth_addr ethzero = {{0,0,0,0,0,0}}; + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +/** the time an ARP entry stays valid after its last update, + * for ARP_TMR_INTERVAL = 5000, this is + * (240 * 5) seconds = 20 minutes. + */ +#define ARP_MAXAGE 240 +/** the time an ARP entry stays pending after first request, + * for ARP_TMR_INTERVAL = 5000, this is + * (2 * 5) seconds = 10 seconds. + * + * @internal Keep this number at least 2, otherwise it might + * run out instantly if the timeout occurs directly after a request. + */ +#define ARP_MAXPENDING 2 + +#define HWTYPE_ETHERNET 1 + +enum etharp_state { + ETHARP_STATE_EMPTY = 0, + ETHARP_STATE_PENDING, + ETHARP_STATE_STABLE +}; + +struct etharp_entry { +#if ARP_QUEUEING + /** Pointer to queue of pending outgoing packets on this ARP entry. */ + struct etharp_q_entry *q; +#else /* ARP_QUEUEING */ + /** Pointer to a single pending outgoing packet on this ARP entry. */ + struct pbuf *q; +#endif /* ARP_QUEUEING */ + ip_addr_t ipaddr; + struct eth_addr ethaddr; +#if LWIP_SNMP || LWIP_ARP + struct netif *netif; +#endif /* LWIP_SNMP */ + u8_t state; + u8_t ctime; +#if ETHARP_SUPPORT_STATIC_ENTRIES + u8_t static_entry; +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ +}; + +static struct etharp_entry arp_table[ARP_TABLE_SIZE]; + +#if !LWIP_NETIF_HWADDRHINT +static u8_t etharp_cached_entry; +#endif /* !LWIP_NETIF_HWADDRHINT */ + +/** Try hard to create a new entry - we want the IP address to appear in + the cache (even if this means removing an active entry or so). */ +#define ETHARP_FLAG_TRY_HARD 1 +#define ETHARP_FLAG_FIND_ONLY 2 +#define ETHARP_FLAG_STATIC_ENTRY 4 + +#if LWIP_NETIF_HWADDRHINT +#define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \ + *((netif)->addr_hint) = (hint); +#else /* LWIP_NETIF_HWADDRHINT */ +#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint)) +#endif /* LWIP_NETIF_HWADDRHINT */ + +static err_t update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags); + + +/* Some checks, instead of etharp_init(): */ +#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) + #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h" +#endif + + +#if ARP_QUEUEING +/** + * Free a complete queue of etharp entries + * + * @param q a qeueue of etharp_q_entry's to free + */ +static void +free_etharp_q(struct etharp_q_entry *q) +{ + struct etharp_q_entry *r; + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("q->p != NULL", q->p != NULL); + while (q) { + r = q; + q = q->next; + LWIP_ASSERT("r->p != NULL", (r->p != NULL)); + pbuf_free(r->p); + memp_free(MEMP_ARP_QUEUE, r); + } +} +#else /* ARP_QUEUEING */ + +/** Compatibility define: free the queued pbuf */ +#define free_etharp_q(q) pbuf_free(q) + +#endif /* ARP_QUEUEING */ + +/** Clean up ARP table entries */ +static void ICACHE_FLASH_ATTR +free_entry(int i) +{ + /* remove from SNMP ARP index tree */ + snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr); + /* and empty packet queue */ + if (arp_table[i].q != NULL) { + /* remove all queued packets */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q))); + free_etharp_q(arp_table[i].q); + arp_table[i].q = NULL; + } + /* recycle entry for re-use */ + arp_table[i].state = ETHARP_STATE_EMPTY; +#if ETHARP_SUPPORT_STATIC_ENTRIES + arp_table[i].static_entry = 0; +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ +#ifdef LWIP_DEBUG + /* for debugging, clean out the complete entry */ + arp_table[i].ctime = 0; +#if LWIP_SNMP + arp_table[i].netif = NULL; +#endif /* LWIP_SNMP */ + ip_addr_set_zero(&arp_table[i].ipaddr); + arp_table[i].ethaddr = ethzero; +#endif /* LWIP_DEBUG */ +} + +/** + * Clears expired entries in the ARP table. + * + * This function should be called every ETHARP_TMR_INTERVAL milliseconds (5 seconds), + * in order to expire entries in the ARP table. + */ +void +etharp_tmr(void) +{ + u8_t i; + + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n")); + /* remove expired entries from the ARP table */ + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + if (state != ETHARP_STATE_EMPTY +#if ETHARP_SUPPORT_STATIC_ENTRIES + && (arp_table[i].static_entry == 0) +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + ) { + arp_table[i].ctime++; + if ((arp_table[i].ctime >= ARP_MAXAGE) || + ((arp_table[i].state == ETHARP_STATE_PENDING) && + (arp_table[i].ctime >= ARP_MAXPENDING))) { + /* pending or stable entry has become old! */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n", + arp_table[i].state == ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i)); + /* clean up entries that have just been expired */ + free_entry(i); + } +#if ARP_QUEUEING + /* still pending entry? (not expired) */ + if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* resend an ARP query here? */ + } +#endif /* ARP_QUEUEING */ + } + } +} + +/** + * Search the ARP table for a matching or new entry. + * + * If an IP address is given, return a pending or stable ARP entry that matches + * the address. If no match is found, create a new entry with this address set, + * but in state ETHARP_EMPTY. The caller must check and possibly change the + * state of the returned entry. + * + * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY. + * + * In all cases, attempt to create new entries from an empty entry. If no + * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle + * old entries. Heuristic choose the least important entry for recycling. + * + * @param ipaddr IP address to find in ARP cache, or to add if not found. + * @param flags @see definition of ETHARP_FLAG_* + * @param netif netif related to this address (used for NETIF_HWADDRHINT) + * + * @return The ARP entry index that matched or is created, ERR_MEM if no + * entry is found or could be recycled. + */ +static s8_t ICACHE_FLASH_ATTR +find_entry(ip_addr_t *ipaddr, u8_t flags) +{ + s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE; + s8_t empty = ARP_TABLE_SIZE; + u8_t i = 0, age_pending = 0, age_stable = 0; + /* oldest entry with packets on queue */ + s8_t old_queue = ARP_TABLE_SIZE; + /* its age */ + u8_t age_queue = 0; + + /** + * a) do a search through the cache, remember candidates + * b) select candidate entry + * c) create new entry + */ + + /* a) in a single search sweep, do all of this + * 1) remember the first empty entry (if any) + * 2) remember the oldest stable entry (if any) + * 3) remember the oldest pending entry without queued packets (if any) + * 4) remember the oldest pending entry with queued packets (if any) + * 5) search for a matching IP entry, either pending or stable + * until 5 matches, or all entries are searched for. + */ + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + /* no empty entry found yet and now we do find one? */ + if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) { + LWIP_DEBUGF(ETHARP_DEBUG, ("find_entry: found empty entry %"U16_F"\n", (u16_t)i)); + /* remember first empty entry */ + empty = i; + } else if (state != ETHARP_STATE_EMPTY) { + LWIP_ASSERT("state == ETHARP_STATE_PENDING || state == ETHARP_STATE_STABLE", + state == ETHARP_STATE_PENDING || state == ETHARP_STATE_STABLE); + /* if given, does IP address match IP address in ARP entry? */ + if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching entry %"U16_F"\n", (u16_t)i)); + /* found exact IP address match, simply bail out */ + return i; + } + /* pending entry? */ + if (state == ETHARP_STATE_PENDING) { + /* pending with queued packets? */ + if (arp_table[i].q != NULL) { + if (arp_table[i].ctime >= age_queue) { + old_queue = i; + age_queue = arp_table[i].ctime; + } + } else + /* pending without queued packets? */ + { + if (arp_table[i].ctime >= age_pending) { + old_pending = i; + age_pending = arp_table[i].ctime; + } + } + /* stable entry? */ + } else if (state == ETHARP_STATE_STABLE) { +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* don't record old_stable for static entries since they never expire */ + if (arp_table[i].static_entry == 0) +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + { + /* remember entry with oldest stable entry in oldest, its age in maxtime */ + if (arp_table[i].ctime >= age_stable) { + old_stable = i; + age_stable = arp_table[i].ctime; + } + } + } + } + } + /* { we have no match } => try to create a new entry */ + + /* don't create new entry, only search? */ + if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) || + /* or no empty entry found and not allowed to recycle? */ + ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n")); + return (s8_t)ERR_MEM; + } + + /* b) choose the least destructive entry to recycle: + * 1) empty entry + * 2) oldest stable entry + * 3) oldest pending entry without queued packets + * 4) oldest pending entry with queued packets + * + * { ETHARP_FLAG_TRY_HARD is set at this point } + */ + + /* 1) empty entry available? */ + if (empty < ARP_TABLE_SIZE) { + i = empty; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i)); + } else { + /* 2) found recyclable stable entry? */ + if (old_stable < ARP_TABLE_SIZE) { + /* recycle oldest stable*/ + i = old_stable; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i)); + /* no queued packets should exist on stable entries */ + LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL); + /* 3) found recyclable pending entry without queued packets? */ + } else if (old_pending < ARP_TABLE_SIZE) { + /* recycle oldest pending */ + i = old_pending; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i)); + /* 4) found recyclable pending entry with queued packets? */ + } else if (old_queue < ARP_TABLE_SIZE) { + /* recycle oldest pending (queued packets are free in free_entry) */ + i = old_queue; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q))); + /* no empty or recyclable entries found */ + } else { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty or recyclable entries found\n")); + return (s8_t)ERR_MEM; + } + + /* { empty or recyclable entry found } */ + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + free_entry(i); + } + + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY", + arp_table[i].state == ETHARP_STATE_EMPTY); + + /* IP address given? */ + if (ipaddr != NULL) { + /* set IP address */ + ip_addr_copy(arp_table[i].ipaddr, *ipaddr); + } + arp_table[i].ctime = 0; +#if ETHARP_SUPPORT_STATIC_ENTRIES + arp_table[i].static_entry = 0; +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + return (err_t)i; +} + +/** + * Send an IP packet on the network using netif->linkoutput + * The ethernet header is filled in before sending. + * + * @params netif the lwIP network interface on which to send the packet + * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header + * @params src the source MAC address to be copied into the ethernet header + * @params dst the destination MAC address to be copied into the ethernet header + * @return ERR_OK if the packet was sent, any other err_t on failure + */ +static err_t ICACHE_FLASH_ATTR +etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst) +{ + struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload; + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); + ETHADDR32_COPY(ðhdr->dest, dst); + ETHADDR16_COPY(ðhdr->src, src); + ethhdr->type = PP_HTONS(ETHTYPE_IP); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p)); + /* send the packet */ + return netif->linkoutput(netif, p); +} + +/** + * Update (or insert) a IP/MAC address pair in the ARP cache. + * + * If a pending entry is resolved, any queued packets will be sent + * at this point. + * + * @param netif netif related to this entry (used for NETIF_ADDRHINT) + * @param ipaddr IP address of the inserted ARP entry. + * @param ethaddr Ethernet address of the inserted ARP entry. + * @param flags @see definition of ETHARP_FLAG_* + * + * @return + * - ERR_OK Succesfully updated ARP cache. + * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set. + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + * @see pbuf_free() + */ +static err_t ICACHE_FLASH_ATTR +update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) +{ + s8_t i; + LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), + ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], + ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); + /* non-unicast address? */ + if (ip_addr_isany(ipaddr) || + ip_addr_isbroadcast(ipaddr, netif) || + ip_addr_ismulticast(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + /* find or create ARP entry */ + i = find_entry(ipaddr, flags); + /* bail out if no entry could be found */ + if (i < 0) { + return (err_t)i; + } + +#if ETHARP_SUPPORT_STATIC_ENTRIES + if (flags & ETHARP_FLAG_STATIC_ENTRY) { + /* record static type */ + arp_table[i].static_entry = 1; + } +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + + /* mark it stable */ + arp_table[i].state = ETHARP_STATE_STABLE; + +#if LWIP_SNMP + /* record network interface */ + arp_table[i].netif = netif; +#endif /* LWIP_SNMP */ + /* insert in SNMP ARP index tree */ + snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); + /* update address */ + ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr); + /* reset time stamp */ + arp_table[i].ctime = 0; + /* this is where we will send out queued packets! */ +#if ARP_QUEUEING + while (arp_table[i].q != NULL) { + struct pbuf *p; + /* remember remainder of queue */ + struct etharp_q_entry *q = arp_table[i].q; + /* pop first item off the queue */ + arp_table[i].q = q->next; + /* get the packet pointer */ + p = q->p; + /* now queue entry can be freed */ + memp_free(MEMP_ARP_QUEUE, q); +#else /* ARP_QUEUEING */ + if (arp_table[i].q != NULL) { + struct pbuf *p = arp_table[i].q; + arp_table[i].q = NULL; +#endif /* ARP_QUEUEING */ + /* send the queued IP packet */ + etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); + /* free the queued IP packet */ + pbuf_free(p); + } + return ERR_OK; +} + +#if ETHARP_SUPPORT_STATIC_ENTRIES +/** Add a new static entry to the ARP table. If an entry exists for the + * specified IP address, this entry is overwritten. + * If packets are queued for the specified IP address, they are sent out. + * + * @param ipaddr IP address for the new static entry + * @param ethaddr ethernet address for the new static entry + * @return @see return values of etharp_add_static_entry + */ +err_t +etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr) +{ + struct netif *netif; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), + ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], + ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); + + netif = ip_route(ipaddr); + if (netif == NULL) { + return ERR_RTE; + } + + return update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY); +} + +/** Remove a static entry from the ARP table previously added with a call to + * etharp_add_static_entry. + * + * @param ipaddr IP address of the static entry to remove + * @return ERR_OK: entry removed + * ERR_MEM: entry wasn't found + * ERR_ARG: entry wasn't a static entry but a dynamic one + */ +err_t +etharp_remove_static_entry(ip_addr_t *ipaddr) +{ + s8_t i; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); + + /* find or create ARP entry */ + i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); + /* bail out if no entry could be found */ + if (i < 0) { + return (err_t)i; + } + + if ((arp_table[i].state != ETHARP_STATE_STABLE) || + (arp_table[i].static_entry == 0)) { + /* entry wasn't a static entry, cannot remove it */ + return ERR_ARG; + } + /* entry found, free it */ + free_entry(i); + return ERR_OK; +} +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +/** + * Remove all ARP table entries of the specified netif. + * + * @param netif points to a network interface + */ +void ICACHE_FLASH_ATTR etharp_cleanup_netif(struct netif *netif) +{ + u8_t i; + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) { + free_entry(i); + } + } +} + +/** + * Finds (stable) ethernet/IP address pair from ARP table + * using interface and IP address index. + * @note the addresses in the ARP table are in network order! + * + * @param netif points to interface index + * @param ipaddr points to the (network order) IP address index + * @param eth_ret points to return pointer + * @param ip_ret points to return pointer + * @return table index if found, -1 otherwise + */ +s8_t +etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, + struct eth_addr **eth_ret, ip_addr_t **ip_ret) +{ + s8_t i; + + LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL", + eth_ret != NULL && ip_ret != NULL); + + LWIP_UNUSED_ARG(netif); + + i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); + if((i >= 0) && arp_table[i].state == ETHARP_STATE_STABLE) { + *eth_ret = &arp_table[i].ethaddr; + *ip_ret = &arp_table[i].ipaddr; + return i; + } + return -1; +} + +#if ETHARP_TRUST_IP_MAC +/** + * Updates the ARP table using the given IP packet. + * + * Uses the incoming IP packet's source address to update the + * ARP cache for the local network. The function does not alter + * or free the packet. This function must be called before the + * packet p is passed to the IP layer. + * + * @param netif The lwIP network interface on which the IP packet pbuf arrived. + * @param p The IP packet that arrived on netif. + * + * @return NULL + * + * @see pbuf_free() + */ +static void ICACHE_FLASH_ATTR +etharp_ip_input(struct netif *netif, struct pbuf *p) +{ + struct eth_hdr *ethhdr; + struct ip_hdr *iphdr; + ip_addr_t iphdr_src; + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + /* Only insert an entry if the source IP address of the + incoming IP packet comes from a host on the local network. */ + ethhdr = (struct eth_hdr *)p->payload; + iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); +#if ETHARP_SUPPORT_VLAN + if (ethhdr->type == ETHTYPE_VLAN) { + iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); + } +#endif /* ETHARP_SUPPORT_VLAN */ + + ip_addr_copy(iphdr_src, iphdr->src); + + /* source is not on the local network? */ + if (!ip_addr_netcmp(&iphdr_src, &(netif->ip_addr), &(netif->netmask))) { + /* do nothing */ + return; + } + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n")); + /* update the source IP address in the cache, if present */ + /* @todo We could use ETHARP_FLAG_TRY_HARD if we think we are going to talk + * back soon (for example, if the destination IP address is ours. */ + update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY); +} +#endif /* ETHARP_TRUST_IP_MAC */ + +/** + * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache + * send out queued IP packets. Updates cache with snooped address pairs. + * + * Should be called for incoming ARP packets. The pbuf in the argument + * is freed by this function. + * + * @param netif The lwIP network interface on which the ARP packet pbuf arrived. + * @param ethaddr Ethernet address of netif. + * @param p The ARP packet that arrived on netif. Is freed by this function. + * + * @return NULL + * + * @see pbuf_free() + */ +static void ICACHE_FLASH_ATTR +etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) +{ + struct etharp_hdr *hdr; + struct eth_hdr *ethhdr; + /* these are aligned properly, whereas the ARP header fields might not be */ + ip_addr_t sipaddr, dipaddr; + u8_t for_us; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ +#ifdef EBUF_LWIP + struct pbuf *q; +#endif /* EBUF_LWIP */ + + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + /* drop short ARP packets: we have to check for p->len instead of p->tot_len here + since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */ + if (p->len < SIZEOF_ETHARP_PACKET) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len, + (s16_t)SIZEOF_ETHARP_PACKET)); + ETHARP_STATS_INC(etharp.lenerr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + return; + } + + ethhdr = (struct eth_hdr *)p->payload; + hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); +#if ETHARP_SUPPORT_VLAN + if (ethhdr->type == ETHTYPE_VLAN) { + hdr = (struct etharp_hdr *)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); + } +#endif /* ETHARP_SUPPORT_VLAN */ + + /* RFC 826 "Packet Reception": */ + if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) || + (hdr->hwlen != ETHARP_HWADDR_LEN) || + (hdr->protolen != sizeof(ip_addr_t)) || + (hdr->proto != PP_HTONS(ETHTYPE_IP)) || + (ethhdr->type != PP_HTONS(ETHTYPE_ARP))) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n", + hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen, ethhdr->type)); + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + return; + } + ETHARP_STATS_INC(etharp.recv); + +#if LWIP_AUTOIP + /* We have to check if a host already has configured our random + * created link local address and continously check if there is + * a host with this IP-address so we can detect collisions */ + autoip_arp_reply(netif, hdr); +#endif /* LWIP_AUTOIP */ + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). */ + IPADDR2_COPY(&sipaddr, &hdr->sipaddr); + IPADDR2_COPY(&dipaddr, &hdr->dipaddr); + + /* this interface is not configured? */ + if (ip_addr_isany(&netif->ip_addr)) { + for_us = 0; + } else { + /* ARP packet directed to us? */ + for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr)); + } + + /* ARP message directed to us? + -> add IP address in ARP cache; assume requester wants to talk to us, + can result in directly sending the queued packets for this host. + ARP message not directed to us? + -> update the source IP address in the cache, if present */ + update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), + for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY); + + /* now act on the message itself */ + switch (hdr->opcode) { + /* ARP request? */ + case PP_HTONS(ARP_REQUEST): + /* ARP request. If it asked for our address, we send out a + * reply. In any case, we time-stamp any existing ARP entry, + * and possiby send out an IP packet that was queued on it. */ + + LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n")); + /* ARP request for our address? */ + if (for_us) { + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n")); + /* Re-use pbuf to send ARP reply. + Since we are re-using an existing pbuf, we can't call etharp_raw since + that would allocate a new pbuf. */ + hdr->opcode = htons(ARP_REPLY); + + IPADDR2_COPY(&hdr->dipaddr, &hdr->sipaddr); + IPADDR2_COPY(&hdr->sipaddr, &netif->ip_addr); + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); +#if LWIP_AUTOIP + /* If we are using Link-Local, all ARP packets that contain a Link-Local + * 'sender IP address' MUST be sent using link-layer broadcast instead of + * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */ + ethdst_hwaddr = ip_addr_islinklocal(&netif->ip_addr) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr; +#endif /* LWIP_AUTOIP */ + + ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr); +#if LWIP_AUTOIP + ETHADDR16_COPY(ðhdr->dest, ethdst_hwaddr); +#else /* LWIP_AUTOIP */ + ETHADDR16_COPY(ðhdr->dest, &hdr->shwaddr); +#endif /* LWIP_AUTOIP */ + ETHADDR16_COPY(&hdr->shwaddr, ethaddr); + ETHADDR16_COPY(ðhdr->src, ethaddr); + + /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header + are already correct, we tested that before */ +#ifdef EBUF_LWIP + /* + * don't do flip-flop here... do a copy here. + * otherwise, we need to handle existing pbuf->eb in ieee80211_output.c + */ + + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (q != NULL) { + pbuf_copy(q, p); + //pbuf_free(p); + } else { + LWIP_ASSERT("q != NULL", q != NULL); + } + + netif->linkoutput(netif, q); + pbuf_free(q); +#else + + /* return ARP reply */ + netif->linkoutput(netif, p); +#endif /* ESF_LWIP */ + /* we are not configured? */ + } else if (ip_addr_isany(&netif->ip_addr)) { + /* { for_us == 0 and netif->ip_addr.addr == 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n")); + /* request was not directed to us */ + } else { + /* { for_us == 0 and netif->ip_addr.addr != 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n")); + } + break; + case PP_HTONS(ARP_REPLY): + /* ARP reply. We already updated the ARP cache earlier. */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n")); +#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) + /* DHCP wants to know about ARP replies from any host with an + * IP address also offered to us by the DHCP server. We do not + * want to take a duplicate IP address on a single network. + * @todo How should we handle redundant (fail-over) interfaces? */ + dhcp_arp_reply(netif, &sipaddr); +#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */ + break; + default: + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode))); + ETHARP_STATS_INC(etharp.err); + break; + } + /* free ARP packet */ + pbuf_free(p); +} + +/** + * Resolve and fill-in Ethernet address header for outgoing IP packet. + * + * For IP multicast and broadcast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, the packet is submitted to etharp_query(). In + * case the IP address is outside the local network, the IP address of + * the gateway is used. + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ipaddr The IP address of the packet destination. + * + * @return + * - ERR_RTE No route to destination (no gateway to external networks), + * or the return type of either etharp_query() or etharp_send_ip(). + */ +err_t +etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr) +{ + struct eth_addr *dest, mcastaddr; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { + /* bail out */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_output: could not allocate room for header.\n")); + LINK_STATS_INC(link.lenerr); + return ERR_BUF; + } + + /* assume unresolved Ethernet address */ + dest = NULL; + /* Determine on destination hardware address. Broadcasts and multicasts + * are special, other IP addresses are looked up in the ARP table. */ + + /* broadcast destination IP address? */ + if (ip_addr_isbroadcast(ipaddr, netif)) { + /* broadcast on Ethernet also */ + dest = (struct eth_addr *)ðbroadcast; + /* multicast destination IP address? */ + } else if (ip_addr_ismulticast(ipaddr)) { + /* Hash IP multicast address to MAC address.*/ + mcastaddr.addr[0] = 0x01; + mcastaddr.addr[1] = 0x00; + mcastaddr.addr[2] = 0x5e; + mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; + mcastaddr.addr[4] = ip4_addr3(ipaddr); + mcastaddr.addr[5] = ip4_addr4(ipaddr); + /* destination Ethernet address is multicast */ + dest = &mcastaddr; + /* unicast destination IP address? */ + } else { + /* outside local network? */ + if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) && + !ip_addr_islinklocal(ipaddr)) { +#if LWIP_AUTOIP + struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload + + sizeof(struct eth_hdr)); + /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with + a link-local source address must always be "directly to its destination + on the same physical link. The host MUST NOT send the packet to any + router for forwarding". */ + if (!ip_addr_islinklocal(&iphdr->src)) +#endif /* LWIP_AUTOIP */ + { + /* interface has default gateway? */ + if (!ip_addr_isany(&netif->gw)) { + /* send to hardware address of default gateway IP address */ + ipaddr = &(netif->gw); + /* no default gateway available */ + } else { + /* no route to destination error (default gateway missing) */ + return ERR_RTE; + } + } + } +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + u8_t etharp_cached_entry = *(netif->addr_hint); + if (etharp_cached_entry < ARP_TABLE_SIZE) { +#endif /* LWIP_NETIF_HWADDRHINT */ + if ((arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE) && + (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr))) { + /* the per-pcb-cached entry is stable and the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), + &arp_table[etharp_cached_entry].ethaddr); + } +#if LWIP_NETIF_HWADDRHINT + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + /* queue on destination Ethernet address belonging to ipaddr */ + return etharp_query(netif, ipaddr, q); + } + + /* continuation for multicast/broadcast destinations */ + /* obtain source Ethernet address of the given interface */ + /* send packet directly on the link */ + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest); +} + +/** + * Send an ARP request for the given IP address and/or queue a packet. + * + * If the IP address was not yet in the cache, a pending ARP cache entry + * is added and an ARP request is sent for the given address. The packet + * is queued on this entry. + * + * If the IP address was already pending in the cache, a new ARP request + * is sent for the given address. The packet is queued on this entry. + * + * If the IP address was already stable in the cache, and a packet is + * given, it is directly sent and no ARP request is sent out. + * + * If the IP address was already stable in the cache, and no packet is + * given, an ARP request is sent out. + * + * @param netif The lwIP network interface on which ipaddr + * must be queried for. + * @param ipaddr The IP address to be resolved. + * @param q If non-NULL, a pbuf that must be delivered to the IP address. + * q is not freed by this function. + * + * @note q must only be ONE packet, not a packet queue! + * + * @return + * - ERR_BUF Could not make room for Ethernet header. + * - ERR_MEM Hardware address unknown, and no more ARP entries available + * to query for address or queue the packet. + * - ERR_MEM Could not queue packet due to memory shortage. + * - ERR_RTE No route to destination (no gateway to external networks). + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + */ +err_t +etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q) +{ + struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr; + err_t result = ERR_MEM; + s8_t i; /* ARP entry index */ + + /* non-unicast address? */ + if (ip_addr_isbroadcast(ipaddr, netif) || + ip_addr_ismulticast(ipaddr) || + ip_addr_isany(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + + /* find entry in ARP cache, ask to create entry if queueing packet */ + i = find_entry(ipaddr, ETHARP_FLAG_TRY_HARD); + + /* could not find or create entry? */ + if (i < 0) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n")); + if (q) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n")); + ETHARP_STATS_INC(etharp.memerr); + } + return (err_t)i; + } + + /* mark a fresh entry as pending (we just sent a request) */ + if (arp_table[i].state == ETHARP_STATE_EMPTY) { + arp_table[i].state = ETHARP_STATE_PENDING; + } + + /* { i is either a STABLE or (new or existing) PENDING entry } */ + LWIP_ASSERT("arp_table[i].state == PENDING or STABLE", + ((arp_table[i].state == ETHARP_STATE_PENDING) || + (arp_table[i].state == ETHARP_STATE_STABLE))); + + /* do we have a pending entry? or an implicit query request? */ + if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) { + /* try to resolve it; send out ARP request */ + result = etharp_request(netif, ipaddr); + if (result != ERR_OK) { + /* ARP request couldn't be sent */ + /* We don't re-send arp request in etharp_tmr, but we still queue packets, + since this failure could be temporary, and the next packet calling + etharp_query again could lead to sending the queued packets. */ + } + if (q == NULL) { + return result; + } + } + + /* packet given? */ + LWIP_ASSERT("q != NULL", q != NULL); + /* stable entry? */ + if (arp_table[i].state == ETHARP_STATE_STABLE) { + /* we have a valid IP->Ethernet address mapping */ + ETHARP_SET_HINT(netif, i); + /* send the packet */ + result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr)); + /* pending entry? (either just created or already pending */ + } else if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* entry is still pending, queue the given packet 'q' */ + struct pbuf *p; + int copy_needed = 0; + /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but + * to copy the whole queue into a new PBUF_RAM (see bug #11400) + * PBUF_ROMs can be left as they are, since ROM must not get changed. */ + p = q; + while (p) { + LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0)); + if(p->type != PBUF_ROM) { + copy_needed = 1; + break; + } + p = p->next; + } + if(copy_needed) { + /* copy the whole packet into new pbufs */ + p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(p != NULL) { + if (pbuf_copy(p, q) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } else { + /* referencing the old pbuf is enough */ + p = q; + pbuf_ref(p); + } + /* packet could be taken over? */ + if (p != NULL) { + /* queue packet ... */ +#if ARP_QUEUEING + struct etharp_q_entry *new_entry; + /* allocate a new arp queue entry */ + new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE); + if (new_entry != NULL) { + new_entry->next = 0; + new_entry->p = p; + if(arp_table[i].q != NULL) { + /* queue was already existent, append the new entry to the end */ + struct etharp_q_entry *r; + r = arp_table[i].q; + while (r->next != NULL) { + r = r->next; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + arp_table[i].q = new_entry; + } + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + result = ERR_OK; + } else { + /* the pool MEMP_ARP_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + result = ERR_MEM; + } +#else /* ARP_QUEUEING */ + /* always queue one packet per ARP request only, freeing a previously queued packet */ + if (arp_table[i].q != NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + pbuf_free(arp_table[i].q); + } + arp_table[i].q = p; + result = ERR_OK; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); +#endif /* ARP_QUEUEING */ + } else { + ETHARP_STATS_INC(etharp.memerr); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + result = ERR_MEM; + } + } + return result; +} + +/** + * Send a raw ARP packet (opcode and all addresses can be modified) + * + * @param netif the lwip network interface on which to send the ARP packet + * @param ethsrc_addr the source MAC address for the ethernet header + * @param ethdst_addr the destination MAC address for the ethernet header + * @param hwsrc_addr the source MAC address for the ARP protocol header + * @param ipsrc_addr the source IP address for the ARP protocol header + * @param hwdst_addr the destination MAC address for the ARP protocol header + * @param ipdst_addr the destination IP address for the ARP protocol header + * @param opcode the type of the ARP packet + * @return ERR_OK if the ARP packet has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +#if !LWIP_AUTOIP +static +#endif /* LWIP_AUTOIP */ +err_t ICACHE_FLASH_ATTR +etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr, + const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr, + const u16_t opcode) +{ + struct pbuf *p; + err_t result = ERR_OK; + struct eth_hdr *ethhdr; + struct etharp_hdr *hdr; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ + + /* allocate a pbuf for the outgoing ARP request packet */ + p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM); + /* could allocate a pbuf for an ARP request? */ + if (p == NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_raw: could not allocate pbuf for ARP request.\n")); + ETHARP_STATS_INC(etharp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr", + (p->len >= SIZEOF_ETHARP_PACKET)); + + ethhdr = (struct eth_hdr *)p->payload; + hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n")); + hdr->opcode = htons(opcode); + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); +#if LWIP_AUTOIP + /* If we are using Link-Local, all ARP packets that contain a Link-Local + * 'sender IP address' MUST be sent using link-layer broadcast instead of + * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */ + ethdst_hwaddr = ip_addr_islinklocal(ipsrc_addr) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr; +#endif /* LWIP_AUTOIP */ + /* Write the ARP MAC-Addresses */ + ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr); + ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr); + /* Write the Ethernet MAC-Addresses */ +#if LWIP_AUTOIP + ETHADDR16_COPY(ðhdr->dest, ethdst_hwaddr); +#else /* LWIP_AUTOIP */ + ETHADDR16_COPY(ðhdr->dest, ethdst_addr); +#endif /* LWIP_AUTOIP */ + ETHADDR16_COPY(ðhdr->src, ethsrc_addr); + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing. */ + IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr); + IPADDR2_COPY(&hdr->dipaddr, ipdst_addr); + + hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET); + hdr->proto = PP_HTONS(ETHTYPE_IP); + /* set hwlen and protolen */ + hdr->hwlen = ETHARP_HWADDR_LEN; + hdr->protolen = sizeof(ip_addr_t); + + ethhdr->type = PP_HTONS(ETHTYPE_ARP); + /* send ARP query */ + result = netif->linkoutput(netif, p); + ETHARP_STATS_INC(etharp.xmit); + /* free ARP query packet */ + pbuf_free(p); + p = NULL; + /* could not allocate pbuf for ARP request */ + + return result; +} + +/** + * Send an ARP request packet asking for ipaddr. + * + * @param netif the lwip network interface on which to send the request + * @param ipaddr the IP address for which to ask + * @return ERR_OK if the request has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +err_t +etharp_request(struct netif *netif, ip_addr_t *ipaddr) +{ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n")); + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->ip_addr, ðzero, + ipaddr, ARP_REQUEST); +} +#endif /* LWIP_ARP */ + +/** + * Process received ethernet frames. Using this function instead of directly + * calling ip_input and passing ARP frames through etharp in ethernetif_input, + * the ARP cache is protected from concurrent access. + * + * @param p the recevied packet, p->payload pointing to the ethernet header + * @param netif the network interface on which the packet was received + */ +err_t +ethernet_input(struct pbuf *p, struct netif *netif) +{ + struct eth_hdr* ethhdr; + u16_t type; + s16_t ip_hdr_offset = SIZEOF_ETH_HDR; + + if (p->len <= SIZEOF_ETH_HDR) { + /* a packet with only an ethernet header (or less) is not valid for us modify by ives at 2014.4.24*/ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } + + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = (struct eth_hdr *)p->payload; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, + ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n", + (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2], + (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5], + (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2], + (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5], + (unsigned)htons(ethhdr->type))); + + type = ethhdr->type; +#if ETHARP_SUPPORT_VLAN + if (type == PP_HTONS(ETHTYPE_VLAN)) { + struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR); + if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) { + /* a packet with only an ethernet/vlan header (or less) is not valid for us modify by ives at 2014.4.24*/ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } +#ifdef ETHARP_VLAN_CHECK /* if not, allow all VLANs */ + if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { + /* silently ignore this packet: not for our VLAN */ + pbuf_free(p); + return ERR_OK; + } +#endif /* ETHARP_VLAN_CHECK */ + type = vlan->tpid; + ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR; + } +#endif /* ETHARP_SUPPORT_VLAN */ + +#if LWIP_ARP_FILTER_NETIF + netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, htons(type)); +#endif /* LWIP_ARP_FILTER_NETIF*/ + + switch (type) { +#if LWIP_ARP + /* IP packet? */ + case PP_HTONS(ETHTYPE_IP): + if (!(netif->flags & NETIF_FLAG_ETHARP)) { + goto free_and_return; + } +#if ETHARP_TRUST_IP_MAC + /* update ARP table */ + etharp_ip_input(netif, p); +#endif /* ETHARP_TRUST_IP_MAC */ + /* skip Ethernet header */ + if(pbuf_header(p, -ip_hdr_offset)) { + LWIP_ASSERT("Can't move over header in packet", 0); + goto free_and_return; + } else { + /* pass to IP layer */ + ip_input(p, netif); + } + break; + + case PP_HTONS(ETHTYPE_ARP): + if (!(netif->flags & NETIF_FLAG_ETHARP)) { + goto free_and_return; + } + /* pass p to ARP module */ + etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p); + break; +#endif /* LWIP_ARP */ +#if PPPOE_SUPPORT + case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */ + pppoe_disc_input(netif, p); + break; + + case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */ + pppoe_data_input(netif, p); + break; +#endif /* PPPOE_SUPPORT */ + + default: + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } + + /* This means the pbuf is freed or consumed, + so the caller doesn't have to free it again */ + return ERR_OK; + +free_and_return: + pbuf_free(p); + return ERR_OK; +} +#endif /* LWIP_ARP || LWIP_ETHERNET */ diff --git a/app/modules/Makefile b/app/modules/Makefile new file mode 100644 index 00000000..1d9f19d9 --- /dev/null +++ b/app/modules/Makefile @@ -0,0 +1,49 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +GEN_LIBS = libmodules.a +endif + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ../libc +INCLUDES += -I ../lua +INCLUDES += -I ../platform +INCLUDES += -I ../wofs +INCLUDES += -I ../spiffs +INCLUDES += -I ../smart +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/modules/adc.c b/app/modules/adc.c new file mode 100644 index 00000000..338a2507 --- /dev/null +++ b/app/modules/adc.c @@ -0,0 +1,44 @@ +// Module for interfacing with adc + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" + +#include "c_types.h" + +// Lua: read(id) , return system adc +static int ICACHE_FLASH_ATTR adc_sample( lua_State* L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( adc, id ); + unsigned val = 0xFFFF & system_adc_read(); + lua_pushinteger( L, val ); + return 1; +} + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE adc_map[] = +{ + { LSTRKEY( "read" ), LFUNCVAL( adc_sample ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_adc( lua_State *L ) +{ +#if LUA_OPTIMIZE_MEMORY > 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_ADC, adc_map ); + // Add constants + + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} diff --git a/app/modules/auxmods.h b/app/modules/auxmods.h new file mode 100644 index 00000000..d8166643 --- /dev/null +++ b/app/modules/auxmods.h @@ -0,0 +1,93 @@ +// Auxiliary Lua modules. All of them are declared here, then each platform +// decides what module(s) to register in the src/platform/xxxxx/platform_conf.h file +// FIXME: no longer platform_conf.h - either CPU header file, or board file + +#ifndef __AUXMODS_H__ +#define __AUXMODS_H__ + +#include "lua.h" + +#define AUXLIB_GPIO "gpio" +LUALIB_API int ( luaopen_gpio )( lua_State *L ); + +#define AUXLIB_SPI "spi" +LUALIB_API int ( luaopen_spi )( lua_State *L ); + +#define AUXLIB_CAN "can" +LUALIB_API int ( luaopen_can )( lua_State *L ); + +#define AUXLIB_TMR "tmr" +LUALIB_API int ( luaopen_tmr )( lua_State *L ); + +#define AUXLIB_PD "pd" +LUALIB_API int ( luaopen_pd )( lua_State *L ); + +#define AUXLIB_UART "uart" +LUALIB_API int ( luaopen_uart )( lua_State *L ); + +#define AUXLIB_TERM "term" +LUALIB_API int ( luaopen_term )( lua_State *L ); + +#define AUXLIB_PWM "pwm" +LUALIB_API int ( luaopen_pwm )( lua_State *L ); + +#define AUXLIB_PACK "pack" +LUALIB_API int ( luaopen_pack )( lua_State *L ); + +#define AUXLIB_BIT "bit" +LUALIB_API int ( luaopen_bit )( lua_State *L ); + +#define AUXLIB_NET "net" +LUALIB_API int ( luaopen_net )( lua_State *L ); + +#define AUXLIB_CPU "cpu" +LUALIB_API int ( luaopen_cpu )( lua_State* L ); + +#define AUXLIB_ADC "adc" +LUALIB_API int ( luaopen_adc )( lua_State *L ); + +#define AUXLIB_RPC "rpc" +LUALIB_API int ( luaopen_rpc )( lua_State *L ); + +#define AUXLIB_BITARRAY "bitarray" +LUALIB_API int ( luaopen_bitarray )( lua_State *L ); + +#define AUXLIB_ELUA "elua" +LUALIB_API int ( luaopen_elua )( lua_State *L ); + +#define AUXLIB_I2C "i2c" +LUALIB_API int ( luaopen_i2c )( lua_State *L ); + +#define AUXLIB_WIFI "wifi" +LUALIB_API int ( luaopen_wifi )( lua_State *L ); + +#define AUXLIB_NODE "node" +LUALIB_API int ( luaopen_node )( lua_State *L ); + +#define AUXLIB_FILE "file" +LUALIB_API int ( luaopen_file )( lua_State *L ); + +#define AUXLIB_OW "ow" +LUALIB_API int ( luaopen_ow )( lua_State *L ); + +// Helper macros +#define MOD_CHECK_ID( mod, id )\ + if( !platform_ ## mod ## _exists( id ) )\ + return luaL_error( L, #mod" %d does not exist", ( unsigned )id ) + +#define MOD_CHECK_TIMER( id )\ + if( id == PLATFORM_TIMER_SYS_ID && !platform_timer_sys_available() )\ + return luaL_error( L, "the system timer is not available on this platform" );\ + if( !platform_timer_exists( id ) )\ + return luaL_error( L, "timer %d does not exist", ( unsigned )id )\ + +#define MOD_CHECK_RES_ID( mod, id, resmod, resid )\ + if( !platform_ ## mod ## _check_ ## resmod ## _id( id, resid ) )\ + return luaL_error( L, #resmod" %d not valid with " #mod " %d", ( unsigned )resid, ( unsigned )id ) + +#define MOD_REG_NUMBER( L, name, val )\ + lua_pushnumber( L, val );\ + lua_setfield( L, -2, name ) + +#endif + diff --git a/app/modules/bit.c b/app/modules/bit.c new file mode 100644 index 00000000..8579cc26 --- /dev/null +++ b/app/modules/bit.c @@ -0,0 +1,145 @@ +/* Bitwise operations library */ +/* (c) Reuben Thomas 2000-2008 */ +/* See README for license */ + +// Modified by BogdanM for eLua + + +#include "c_limits.h" + +//#include "lua.h" +#include "lauxlib.h" +#include "auxmods.h" +// #include "type.h" +#include "lrotable.h" + +/* FIXME: Assume size_t is an unsigned lua_Integer */ +typedef size_t lua_UInteger; +#define LUA_UINTEGER_MAX SIZE_MAX + +/* Define TOBIT to get a bit value */ +#define TOBIT(L, n) \ + (luaL_checkinteger((L), (n))) + +/* Operations + + The macros MONADIC and VARIADIC only deal with bitwise operations. + + LOGICAL_SHIFT truncates its left-hand operand before shifting so + that any extra bits at the most-significant end are not shifted + into the result. + + ARITHMETIC_SHIFT does not truncate its left-hand operand, so that + the sign bits are not removed and right shift work properly. + */ + +#define MONADIC(name, op) \ + static int ICACHE_FLASH_ATTR bit_ ## name(lua_State *L) { \ + lua_pushinteger(L, op TOBIT(L, 1)); \ + return 1; \ + } + +#define VARIADIC(name, op) \ + static int ICACHE_FLASH_ATTR bit_ ## name(lua_State *L) { \ + int n = lua_gettop(L), i; \ + lua_Integer w = TOBIT(L, 1); \ + for (i = 2; i <= n; i++) \ + w op TOBIT(L, i); \ + lua_pushinteger(L, w); \ + return 1; \ + } + +#define LOGICAL_SHIFT(name, op) \ + static int ICACHE_FLASH_ATTR bit_ ## name(lua_State *L) { \ + lua_pushinteger(L, (lua_UInteger)TOBIT(L, 1) op \ + (unsigned)luaL_checknumber(L, 2)); \ + return 1; \ + } + +#define ARITHMETIC_SHIFT(name, op) \ + static int ICACHE_FLASH_ATTR bit_ ## name(lua_State *L) { \ + lua_pushinteger(L, (lua_Integer)TOBIT(L, 1) op \ + (unsigned)luaL_checknumber(L, 2)); \ + return 1; \ + } + +MONADIC(bnot, ~) +VARIADIC(band, &=) +VARIADIC(bor, |=) +VARIADIC(bxor, ^=) +ARITHMETIC_SHIFT(lshift, <<) +LOGICAL_SHIFT(rshift, >>) +ARITHMETIC_SHIFT(arshift, >>) + +// Lua: res = bit( position ) +static int ICACHE_FLASH_ATTR bit_bit( lua_State* L ) +{ + lua_pushinteger( L, ( lua_Integer )( 1 << luaL_checkinteger( L, 1 ) ) ); + return 1; +} + +// Lua: res = isset( value, position ) +static int ICACHE_FLASH_ATTR bit_isset( lua_State* L ) +{ + lua_UInteger val = ( lua_UInteger )luaL_checkinteger( L, 1 ); + unsigned pos = ( unsigned )luaL_checkinteger( L, 2 ); + + lua_pushboolean( L, val & ( 1 << pos ) ? 1 : 0 ); + return 1; +} + +// Lua: res = isclear( value, position ) +static int ICACHE_FLASH_ATTR bit_isclear( lua_State* L ) +{ + lua_UInteger val = ( lua_UInteger )luaL_checkinteger( L, 1 ); + unsigned pos = ( unsigned )luaL_checkinteger( L, 2 ); + + lua_pushboolean( L, val & ( 1 << pos ) ? 0 : 1 ); + return 1; +} + +// Lua: res = set( value, pos1, pos2, ... ) +static int ICACHE_FLASH_ATTR bit_set( lua_State* L ) +{ + lua_UInteger val = ( lua_UInteger )luaL_checkinteger( L, 1 ); + unsigned total = lua_gettop( L ), i; + + for( i = 2; i <= total; i ++ ) + val |= 1 << ( unsigned )luaL_checkinteger( L, i ); + lua_pushinteger( L, ( lua_Integer )val ); + return 1; +} + +// Lua: res = clear( value, pos1, pos2, ... ) +static int ICACHE_FLASH_ATTR bit_clear( lua_State* L ) +{ + lua_UInteger val = ( lua_UInteger )luaL_checkinteger( L, 1 ); + unsigned total = lua_gettop( L ), i; + + for( i = 2; i <= total; i ++ ) + val &= ~( 1 << ( unsigned )luaL_checkinteger( L, i ) ); + lua_pushinteger( L, ( lua_Integer )val ); + return 1; +} + +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE bit_map[] = { + { LSTRKEY( "bnot" ), LFUNCVAL( bit_bnot ) }, + { LSTRKEY( "band" ), LFUNCVAL( bit_band ) }, + { LSTRKEY( "bor" ), LFUNCVAL( bit_bor ) }, + { LSTRKEY( "bxor" ), LFUNCVAL( bit_bxor ) }, + { LSTRKEY( "lshift" ), LFUNCVAL( bit_lshift ) }, + { LSTRKEY( "rshift" ), LFUNCVAL( bit_rshift ) }, + { LSTRKEY( "arshift" ), LFUNCVAL( bit_arshift ) }, + { LSTRKEY( "bit" ), LFUNCVAL( bit_bit ) }, + { LSTRKEY( "set" ), LFUNCVAL( bit_set ) }, + { LSTRKEY( "clear" ), LFUNCVAL( bit_clear ) }, + { LSTRKEY( "isset" ), LFUNCVAL( bit_isset ) }, + { LSTRKEY( "isclear" ), LFUNCVAL( bit_isclear ) }, + { LNILKEY, LNILVAL} +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_bit (lua_State *L) { + LREGISTER( L, "bit", bit_map ); +} diff --git a/app/modules/file.c b/app/modules/file.c new file mode 100644 index 00000000..9edc6fd5 --- /dev/null +++ b/app/modules/file.c @@ -0,0 +1,262 @@ +// Module for interfacing with file system + +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" + +#include "c_types.h" +#include "flash_fs.h" +#include "c_string.h" + +static int file_fd = FS_OPEN_OK - 1; + +// Lua: open(filename, mode) +static int ICACHE_FLASH_ATTR file_open( lua_State* L ) +{ + size_t len; + if((FS_OPEN_OK - 1)!=file_fd){ + fs_close(file_fd); + file_fd = FS_OPEN_OK - 1; + } + + const char *fname = luaL_checklstring( L, 1, &len ); + if( len > FS_NAME_MAX_LENGTH ) + return luaL_error(L, "filename too long"); + const char *mode = luaL_optstring(L, 2, "r"); + + file_fd = fs_open(fname, fs_mode2flag(mode)); + + if(file_fd < FS_OPEN_OK){ + file_fd = FS_OPEN_OK - 1; + lua_pushnil(L); + } else { + lua_pushboolean(L, 1); + } + return 1; +} + +// Lua: close() +static int ICACHE_FLASH_ATTR file_close( lua_State* L ) +{ + if((FS_OPEN_OK - 1)!=file_fd){ + fs_close(file_fd); + file_fd = FS_OPEN_OK - 1; + } + return 0; +} + +#if defined(BUILD_WOFS) +// Lua: list() +static int ICACHE_FLASH_ATTR file_list( lua_State* L ) +{ + uint32_t start = 0; + size_t act_len = 0; + char fsname[ FS_NAME_MAX_LENGTH + 1 ]; + lua_newtable( L ); + while( FS_FILE_OK == wofs_next(&start, fsname, FS_NAME_MAX_LENGTH, &act_len) ){ + lua_pushinteger(L, act_len); + lua_setfield( L, -2, fsname ); + } + return 1; +} + +// Lua: format() +static int ICACHE_FLASH_ATTR file_format( lua_State* L ) +{ + size_t len; + file_close(L); + if( !fs_format() ) + { + NODE_ERR( "\ni*** ERROR ***: unable to format. FS might be compromised.\n" ); + NODE_ERR( "It is advised to re-flash the nodeMcu image.\n" ); + } + else{ + NODE_ERR( "format done.\n" ); + } + return 0; +} + +#elif defined(BUILD_SPIFFS) + +extern spiffs fs; + +// Lua: list() +static int ICACHE_FLASH_ATTR file_list( lua_State* L ) +{ + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + + lua_newtable( L ); + SPIFFS_opendir(&fs, "/", &d); + while ((pe = SPIFFS_readdir(&d, pe))) { + // NODE_ERR(" %s size:%i\n", pe->name, pe->size); + lua_pushinteger(L, pe->size); + lua_setfield( L, -2, pe->name ); + } + SPIFFS_closedir(&d); + return 1; +} + +static int ICACHE_FLASH_ATTR file_seek (lua_State *L) +{ + static const int mode[] = {FS_SEEK_SET, FS_SEEK_CUR, FS_SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + if((FS_OPEN_OK - 1)==file_fd) + return luaL_error(L, "open a file first"); + int op = luaL_checkoption(L, 1, "cur", modenames); + long offset = luaL_optlong(L, 2, 0); + op = fs_seek(file_fd, offset, mode[op]); + if (op) + lua_pushboolean(L, 1); /* error */ + else + lua_pushinteger(L, fs_tell(file_fd)); + return 1; +} + +// Lua: remove(filename) +static int ICACHE_FLASH_ATTR file_remove( lua_State* L ) +{ + size_t len; + const char *fname = luaL_checklstring( L, 1, &len ); + if( len > FS_NAME_MAX_LENGTH ) + return luaL_error(L, "filename too long"); + file_close(L); + SPIFFS_remove(&fs, fname); + return 0; +} + +// Lua: flush() +static int ICACHE_FLASH_ATTR file_flush( lua_State* L ) +{ + if((FS_OPEN_OK - 1)==file_fd) + return luaL_error(L, "open a file first"); + if(fs_flush(file_fd) == 0) + lua_pushboolean(L, 1); + else + lua_pushnil(L); + return 1; +} +#if 0 +// Lua: check() +static int ICACHE_FLASH_ATTR file_check( lua_State* L ) +{ + file_close(L); + lua_pushinteger(L, fs_check()); + return 1; +} +#endif + +#endif + +// Lua: readline() +static int ICACHE_FLASH_ATTR file_readline( lua_State* L ) +{ + luaL_Buffer b; + if((FS_OPEN_OK - 1)==file_fd) + return luaL_error(L, "open a file first"); + + luaL_buffinit(L, &b); + char *p = luaL_prepbuffer(&b); + signed char c = EOF; + int i = 0; + + do{ + c = (signed char)fs_getc(file_fd); + if(c==EOF){ + break; + } + p[i++] = c; + }while((c!=EOF) && (c!='\n') && (i0 && p[i-1] == '\n') + i--; /* do not include `eol' */ +#endif + + if(i==0){ + luaL_pushresult(&b); /* close buffer */ + return (lua_objlen(L, -1) > 0); /* check whether read something */ + } + + luaL_addsize(&b, i); + luaL_pushresult(&b); /* close buffer */ + return 1; /* read at least an `eol' */ +} + +// Lua: write("string") +static int ICACHE_FLASH_ATTR file_write( lua_State* L ) +{ + if((FS_OPEN_OK - 1)==file_fd) + return luaL_error(L, "open a file first"); + size_t l, rl; + const char *s = luaL_checklstring(L, 1, &l); + rl = fs_write(file_fd, s, l); + if(rl==l) + lua_pushboolean(L, 1); + else + lua_pushnil(L); + return 1; +} + +// Lua: writeline("string") +static int ICACHE_FLASH_ATTR file_writeline( lua_State* L ) +{ + if((FS_OPEN_OK - 1)==file_fd) + return luaL_error(L, "open a file first"); + size_t l, rl; + const char *s = luaL_checklstring(L, 1, &l); + rl = fs_write(file_fd, s, l); + if(rl==l){ + rl = fs_write(file_fd, "\n", 1); + if(rl==1) + lua_pushboolean(L, 1); + else + lua_pushnil(L); + } + else{ + lua_pushnil(L); + } + return 1; +} + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE file_map[] = +{ + { LSTRKEY( "list" ), LFUNCVAL( file_list ) }, + { LSTRKEY( "open" ), LFUNCVAL( file_open ) }, + { LSTRKEY( "close" ), LFUNCVAL( file_close ) }, + { LSTRKEY( "write" ), LFUNCVAL( file_write ) }, + { LSTRKEY( "writeline" ), LFUNCVAL( file_writeline ) }, + { LSTRKEY( "readline" ), LFUNCVAL( file_readline ) }, +#if defined(BUILD_WOFS) + { LSTRKEY( "format" ), LFUNCVAL( file_format ) }, +#elif defined(BUILD_SPIFFS) + { LSTRKEY( "remove" ), LFUNCVAL( file_remove ) }, + { LSTRKEY( "seek" ), LFUNCVAL( file_seek ) }, + { LSTRKEY( "flush" ), LFUNCVAL( file_flush ) }, + // { LSTRKEY( "check" ), LFUNCVAL( file_check ) }, +#endif + +#if LUA_OPTIMIZE_MEMORY > 0 + +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_file( lua_State *L ) +{ +#if LUA_OPTIMIZE_MEMORY > 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_NODE, file_map ); + // Add constants + + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} diff --git a/app/modules/gpio.c b/app/modules/gpio.c new file mode 100644 index 00000000..e3fa319c --- /dev/null +++ b/app/modules/gpio.c @@ -0,0 +1,200 @@ +// Module for interfacing with GPIO + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" + +#include "c_types.h" +#include "c_string.h" + +#define PULLUP PLATFORM_GPIO_PULLUP +#define FLOAT PLATFORM_GPIO_FLOAT +#define OUTPUT PLATFORM_GPIO_OUTPUT +#define INPUT PLATFORM_GPIO_INPUT +#define INTERRUPT PLATFORM_GPIO_INT +#define HIGH PLATFORM_GPIO_HIGH +#define LOW PLATFORM_GPIO_LOW + + +#ifdef GPIO_INTERRUPT_ENABLE +static int gpio_cb_ref[GPIO_PIN_NUM]; +static lua_State* gL = NULL; + +void ICACHE_FLASH_ATTR lua_gpio_unref(unsigned pin){ + if(gpio_cb_ref[pin] != LUA_NOREF){ + if(gL!=NULL) + luaL_unref(gL, LUA_REGISTRYINDEX, gpio_cb_ref[pin]); + } + gpio_cb_ref[pin] = LUA_NOREF; +} + +void ICACHE_FLASH_ATTR gpio_intr_callback( unsigned pin, unsigned level ) +{ + NODE_DBG("pin:%d, level:%d \n", pin, level); + if(gpio_cb_ref[pin] == LUA_NOREF) + return; + if(!gL) + return; + lua_rawgeti(gL, LUA_REGISTRYINDEX, gpio_cb_ref[pin]); + lua_pushinteger(gL, level); + lua_call(gL, 1, 0); +} + +// Lua: trig( pin, type, function ) +static int ICACHE_FLASH_ATTR lgpio_trig( lua_State* L ) +{ + unsigned type; + unsigned pin; + size_t sl; + + pin = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( gpio, pin ); + if(pin==0) + return luaL_error( L, "no interrupt for D0" ); + + const char *str = luaL_checklstring( L, 2, &sl ); + if (str == NULL) + return luaL_error( L, "wrong arg type" ); + + if(sl == 2 && c_strcmp(str, "up") == 0){ + type = GPIO_PIN_INTR_POSEDGE; + }else if(sl == 4 && c_strcmp(str, "down") == 0){ + type = GPIO_PIN_INTR_NEGEDGE; + }else if(sl == 4 && c_strcmp(str, "both") == 0){ + type = GPIO_PIN_INTR_ANYEGDE; + }else if(sl == 3 && c_strcmp(str, "low") == 0){ + type = GPIO_PIN_INTR_LOLEVEL; + }else if(sl == 4 && c_strcmp(str, "high") == 0){ + type = GPIO_PIN_INTR_HILEVEL; + }else{ + type = GPIO_PIN_INTR_DISABLE; + } + + // luaL_checkanyfunction(L, 3); + if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION){ + lua_pushvalue(L, 3); // copy argument (func) to the top of stack + if(gpio_cb_ref[pin] != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]); + gpio_cb_ref[pin] = luaL_ref(L, LUA_REGISTRYINDEX); + } + + platform_gpio_intr_init(pin, type); + return 0; +} +#endif + +// Lua: mode( pin, mode, pullup ) +static int ICACHE_FLASH_ATTR lgpio_mode( lua_State* L ) +{ + unsigned mode, pullup = FLOAT; + unsigned pin; + + pin = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( gpio, pin ); + mode = luaL_checkinteger( L, 2 ); + if ( mode!=OUTPUT && mode!=INPUT && mode!=INTERRUPT) + return luaL_error( L, "wrong arg type" ); + if(pin==0 && mode==INTERRUPT) + return luaL_error( L, "no interrupt for D0" ); + if(lua_isnumber(L, 3)) + pullup = lua_tointeger( L, 3 ); + if(pullup!=FLOAT) + pullup = PULLUP; +#ifdef GPIO_INTERRUPT_ENABLE + gL = L; // save to local gL, for callback function + if (mode!=INTERRUPT){ // disable interrupt + if(gpio_cb_ref[pin] != LUA_NOREF){ + luaL_unref(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]); + } + gpio_cb_ref[pin] = LUA_NOREF; + } +#endif + int r = platform_gpio_mode( pin, mode, pullup ); + if( r<0 ) + return luaL_error( L, "wrong pin num." ); + return 0; +} + +// Lua: read( pin ) +static int ICACHE_FLASH_ATTR lgpio_read( lua_State* L ) +{ + unsigned pin; + + pin = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( gpio, pin ); + + unsigned level = platform_gpio_read( pin ); + lua_pushinteger( L, level ); + return 1; +} + +// Lua: write( pin, level ) +static int ICACHE_FLASH_ATTR lgpio_write( lua_State* L ) +{ + unsigned level; + unsigned pin; + + pin = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( gpio, pin ); + level = luaL_checkinteger( L, 2 ); + if ( level!=HIGH && level!=LOW ) + return luaL_error( L, "wrong arg type" ); + platform_gpio_write(pin, level); + return 0; +} + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE gpio_map[] = +{ + { LSTRKEY( "mode" ), LFUNCVAL( lgpio_mode ) }, + { LSTRKEY( "read" ), LFUNCVAL( lgpio_read ) }, + { LSTRKEY( "write" ), LFUNCVAL( lgpio_write ) }, +#ifdef GPIO_INTERRUPT_ENABLE + { LSTRKEY( "trig" ), LFUNCVAL( lgpio_trig ) }, +#endif +#if LUA_OPTIMIZE_MEMORY > 0 +#ifdef GPIO_INTERRUPT_ENABLE + { LSTRKEY( "INT" ), LNUMVAL( INTERRUPT ) }, +#endif + { LSTRKEY( "OUTPUT" ), LNUMVAL( OUTPUT ) }, + { LSTRKEY( "INPUT" ), LNUMVAL( INPUT ) }, + { LSTRKEY( "HIGH" ), LNUMVAL( HIGH ) }, + { LSTRKEY( "LOW" ), LNUMVAL( LOW ) }, + { LSTRKEY( "FLOAT" ), LNUMVAL( FLOAT ) }, + { LSTRKEY( "PULLUP" ), LNUMVAL( PULLUP ) }, +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_gpio( lua_State *L ) +{ +#ifdef GPIO_INTERRUPT_ENABLE + int i; + for(i=0;i 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_GPIO, gpio_map ); + // Add constants +#ifdef GPIO_INTERRUPT_ENABLE + MOD_REG_NUMBER( L, "INT", INTERRUPT ); +#endif + MOD_REG_NUMBER( L, "OUTPUT", OUTPUT ); + MOD_REG_NUMBER( L, "INPUT", INPUT ); + MOD_REG_NUMBER( L, "HIGH", HIGH ); + MOD_REG_NUMBER( L, "LOW", LOW ); + MOD_REG_NUMBER( L, "FLOAT", FLOAT ); + MOD_REG_NUMBER( L, "PULLUP", PULLUP ); + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} diff --git a/app/modules/i2c.c b/app/modules/i2c.c new file mode 100644 index 00000000..ae8f1fbc --- /dev/null +++ b/app/modules/i2c.c @@ -0,0 +1,181 @@ +// Module for interfacing with the I2C interface + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" + +// Lua: speed = i2c.setup( id, sda, scl, speed ) +static int ICACHE_FLASH_ATTR i2c_setup( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + unsigned sda = luaL_checkinteger( L, 2 ); + unsigned scl = luaL_checkinteger( L, 3 ); + + MOD_CHECK_ID( i2c, id ); + MOD_CHECK_ID( gpio, sda ); + MOD_CHECK_ID( gpio, scl ); + + if(scl==0 || sda==0) + return luaL_error( L, "no i2c for D0" ); + + s32 speed = ( s32 )luaL_checkinteger( L, 4 ); + if (speed <= 0) + return luaL_error( L, "wrong arg range" ); + lua_pushinteger( L, platform_i2c_setup( id, sda, scl, (u32)speed ) ); + return 1; +} + +// Lua: i2c.start( id ) +static int ICACHE_FLASH_ATTR i2c_start( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + + MOD_CHECK_ID( i2c, id ); + platform_i2c_send_start( id ); + return 0; +} + +// Lua: i2c.stop( id ) +static int ICACHE_FLASH_ATTR i2c_stop( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + + MOD_CHECK_ID( i2c, id ); + platform_i2c_send_stop( id ); + return 0; +} + +// Lua: status = i2c.address( id, address, direction ) +static int ICACHE_FLASH_ATTR i2c_address( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + int address = luaL_checkinteger( L, 2 ); + int direction = luaL_checkinteger( L, 3 ); + + MOD_CHECK_ID( i2c, id ); + if ( address < 0 || address > 127 ) + return luaL_error( L, "wrong arg range" ); + lua_pushboolean( L, platform_i2c_send_address( id, (u16)address, direction ) ); + return 1; +} + +// Lua: wrote = i2c.write( id, data1, [data2], ..., [datan] ) +// data can be either a string, a table or an 8-bit number +static int ICACHE_FLASH_ATTR i2c_write( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + const char *pdata; + size_t datalen, i; + int numdata; + u32 wrote = 0; + unsigned argn; + + MOD_CHECK_ID( i2c, id ); + if( lua_gettop( L ) < 2 ) + return luaL_error( L, "wrong arg type" ); + for( argn = 2; argn <= lua_gettop( L ); argn ++ ) + { + // lua_isnumber() would silently convert a string of digits to an integer + // whereas here strings are handled separately. + if( lua_type( L, argn ) == LUA_TNUMBER ) + { + numdata = ( int )luaL_checkinteger( L, argn ); + if( numdata < 0 || numdata > 255 ) + return luaL_error( L, "wrong arg range" ); + if( platform_i2c_send_byte( id, numdata ) != 1 ) + break; + wrote ++; + } + else if( lua_istable( L, argn ) ) + { + datalen = lua_objlen( L, argn ); + for( i = 0; i < datalen; i ++ ) + { + lua_rawgeti( L, argn, i + 1 ); + numdata = ( int )luaL_checkinteger( L, -1 ); + lua_pop( L, 1 ); + if( numdata < 0 || numdata > 255 ) + return luaL_error( L, "wrong arg range" ); + if( platform_i2c_send_byte( id, numdata ) == 0 ) + break; + } + wrote += i; + if( i < datalen ) + break; + } + else + { + pdata = luaL_checklstring( L, argn, &datalen ); + for( i = 0; i < datalen; i ++ ) + if( platform_i2c_send_byte( id, pdata[ i ] ) == 0 ) + break; + wrote += i; + if( i < datalen ) + break; + } + } + lua_pushinteger( L, wrote ); + return 1; +} + +// Lua: read = i2c.read( id, size ) +static int ICACHE_FLASH_ATTR i2c_read( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + u32 size = ( u32 )luaL_checkinteger( L, 2 ), i; + luaL_Buffer b; + int data; + + MOD_CHECK_ID( i2c, id ); + if( size == 0 ) + return 0; + luaL_buffinit( L, &b ); + for( i = 0; i < size; i ++ ) + if( ( data = platform_i2c_recv_byte( id, i < size - 1 ) ) == -1 ) + break; + else + luaL_addchar( &b, ( char )data ); + luaL_pushresult( &b ); + return 1; +} + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE i2c_map[] = +{ + { LSTRKEY( "setup" ), LFUNCVAL( i2c_setup ) }, + { LSTRKEY( "start" ), LFUNCVAL( i2c_start ) }, + { LSTRKEY( "stop" ), LFUNCVAL( i2c_stop ) }, + { LSTRKEY( "address" ), LFUNCVAL( i2c_address ) }, + { LSTRKEY( "write" ), LFUNCVAL( i2c_write ) }, + { LSTRKEY( "read" ), LFUNCVAL( i2c_read ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + // { LSTRKEY( "FAST" ), LNUMVAL( PLATFORM_I2C_SPEED_FAST ) }, + { LSTRKEY( "SLOW" ), LNUMVAL( PLATFORM_I2C_SPEED_SLOW ) }, + { LSTRKEY( "TRANSMITTER" ), LNUMVAL( PLATFORM_I2C_DIRECTION_TRANSMITTER ) }, + { LSTRKEY( "RECEIVER" ), LNUMVAL( PLATFORM_I2C_DIRECTION_RECEIVER ) }, +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_i2c( lua_State *L ) +{ +#if LUA_OPTIMIZE_MEMORY > 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_I2C, i2c_map ); + + // Add the stop bits and parity constants (for i2c.setup) + // MOD_REG_NUMBER( L, "FAST", PLATFORM_I2C_SPEED_FAST ); + MOD_REG_NUMBER( L, "SLOW", PLATFORM_I2C_SPEED_SLOW ); + MOD_REG_NUMBER( L, "TRANSMITTER", PLATFORM_I2C_DIRECTION_TRANSMITTER ); + MOD_REG_NUMBER( L, "RECEIVER", PLATFORM_I2C_DIRECTION_RECEIVER ); + + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} + diff --git a/app/modules/modules.h b/app/modules/modules.h new file mode 100644 index 00000000..e58ca748 --- /dev/null +++ b/app/modules/modules.h @@ -0,0 +1,119 @@ +/** + * External modules library + */ + +#ifndef __MODULES_H__ +#define __MODULES_H__ + +#if defined(LUA_USE_MODULES_GPIO) +#define MODULES_GPIO "gpio" +#define ROM_MODULES_GPIO \ + _ROM(MODULES_GPIO, luaopen_gpio, gpio_map) +#else +#define ROM_MODULES_GPIO +#endif + +#if defined(LUA_USE_MODULES_PWM) +#define MODULES_PWM "pwm" +#define ROM_MODULES_PWM \ + _ROM(MODULES_PWM, luaopen_pwm, pwm_map) +#else +#define ROM_MODULES_PWM +#endif + +#if defined(LUA_USE_MODULES_WIFI) +#define MODULES_WIFI "wifi" +#define ROM_MODULES_WIFI \ + _ROM(MODULES_WIFI, luaopen_wifi, wifi_map) +#else +#define ROM_MODULES_WIFI +#endif + +#if defined(LUA_USE_MODULES_NET) +#define MODULES_NET "net" +#define ROM_MODULES_NET \ + _ROM(MODULES_NET, luaopen_net, net_map) +#else +#define ROM_MODULES_NET +#endif + +#if defined(LUA_USE_MODULES_I2C) +#define MODULES_I2C "i2c" +#define ROM_MODULES_I2C \ + _ROM(MODULES_I2C, luaopen_i2c, i2c_map) +#else +#define ROM_MODULES_I2C +#endif + +#if defined(LUA_USE_MODULES_TMR) +#define MODULES_TMR "tmr" +#define ROM_MODULES_TMR \ + _ROM(MODULES_TMR, luaopen_tmr, tmr_map) +#else +#define ROM_MODULES_TMR +#endif + +#if defined(LUA_USE_MODULES_NODE) +#define MODULES_NODE "node" +#define ROM_MODULES_NODE \ + _ROM(MODULES_NODE, luaopen_node, node_map) +#else +#define ROM_MODULES_NODE +#endif + +#if defined(LUA_USE_MODULES_FILE) +#define MODULES_FILE "file" +#define ROM_MODULES_FILE \ + _ROM(MODULES_FILE, luaopen_file, file_map) +#else +#define ROM_MODULES_FILE +#endif + +#if defined(LUA_USE_MODULES_ADC) +#define MODULES_ADC "adc" +#define ROM_MODULES_ADC \ + _ROM(MODULES_ADC, luaopen_adc, adc_map) +#else +#define ROM_MODULES_ADC +#endif + +#if defined(LUA_USE_MODULES_UART) +#define MODULES_UART "uart" +#define ROM_MODULES_UART \ + _ROM(MODULES_UART, luaopen_uart, uart_map) +#else +#define ROM_MODULES_UART +#endif + +#if defined(LUA_USE_MODULES_OW) +#define MODULES_OW "ow" +#define ROM_MODULES_OW \ + _ROM(MODULES_OW, luaopen_ow, ow_map) +#else +#define ROM_MODULES_OW +#endif + +#if defined(LUA_USE_MODULES_BIT) +#define MODULES_BIT "bit" +#define ROM_MODULES_BIT \ + _ROM(MODULES_BIT, luaopen_bit, bit_map) +#else +#define ROM_MODULES_BIT +#endif + +#define LUA_MODULES_ROM \ + ROM_MODULES_GPIO \ + ROM_MODULES_PWM \ + ROM_MODULES_WIFI \ + ROM_MODULES_I2C \ + ROM_MODULES_TMR \ + ROM_MODULES_NODE \ + ROM_MODULES_FILE \ + ROM_MODULES_NET \ + ROM_MODULES_ADC \ + ROM_MODULES_UART \ + ROM_MODULES_OW \ + ROM_MODULES_BIT + +#endif + diff --git a/app/modules/net.c b/app/modules/net.c new file mode 100644 index 00000000..125b2f62 --- /dev/null +++ b/app/modules/net.c @@ -0,0 +1,1359 @@ +// Module for network + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" + +#include "c_string.h" +#include "c_stdlib.h" + +#include "c_types.h" +#include "mem.h" +#include "espconn.h" + +#ifdef CLIENT_SSL_ENABLE +unsigned char *default_certificate; +unsigned int default_certificate_len = 0; +unsigned char *default_private_key; +unsigned int default_private_key_len = 0; +#endif + +#define TCP ESPCONN_TCP +#define UDP ESPCONN_UDP + +static ip_addr_t host_ip; // for dns + +#if 0 +static int expose_array(lua_State* L, char *array, unsigned short len); +#endif + +#define MAX_SOCKET 5 +static int socket_num = 0; +static int socket[MAX_SOCKET]; +static lua_State *gL = NULL; +static int tcpserver_cb_connect_ref = LUA_NOREF; // for tcp server connected callback +static uint16_t tcp_server_timeover = 30; + +static struct espconn *pTcpServer = NULL; +static struct espconn *pUdpServer = NULL; + +typedef struct lnet_userdata +{ + struct espconn *pesp_conn; + int self_ref; + int cb_connect_ref; + int cb_reconnect_ref; + int cb_disconnect_ref; + int cb_receive_ref; + int cb_send_ref; + int cb_dns_found_ref; +#ifdef CLIENT_SSL_ENABLE + uint8_t secure; +#endif +}lnet_userdata; + +static void ICACHE_FLASH_ATTR +net_server_disconnected(void *arg) // for tcp server only +{ + NODE_DBG("net_server_disconnected is called.\n"); + struct espconn *pesp_conn = arg; + if(pesp_conn == NULL) + return; + lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse; + if(nud == NULL) + return; + if(gL == NULL) + return; +#if 0 + char temp[20] = {0}; + c_sprintf(temp, IPSTR, IP2STR( &(pesp_conn->proto.tcp->remote_ip) ) ); + NODE_DBG("remote "); + NODE_DBG(temp); + NODE_DBG(":"); + NODE_DBG("%d",pesp_conn->proto.tcp->remote_port); + NODE_DBG(" disconnected.\n"); +#endif + if(nud->cb_disconnect_ref != LUA_NOREF && nud->self_ref != LUA_NOREF) + { + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_disconnect_ref); + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(client) to callback func in lua + lua_call(gL, 1, 0); + } + int i; + lua_gc(gL, LUA_GCSTOP, 0); + for(i=0;iself_ref) ){ + // found the saved client + nud->pesp_conn->reverse = NULL; + nud->pesp_conn = NULL; // the espconn is made by low level sdk, do not need to free, delete() will not free it. + nud->self_ref = LUA_NOREF; // unref this, and the net.socket userdata will delete it self + luaL_unref(gL, LUA_REGISTRYINDEX, socket[i]); + socket[i] = LUA_NOREF; + socket_num--; + break; + } + } + lua_gc(gL, LUA_GCRESTART, 0); +} + +static void ICACHE_FLASH_ATTR +net_socket_disconnected(void *arg) // tcp only +{ + NODE_DBG("net_socket_disconnected is called.\n"); + struct espconn *pesp_conn = arg; + if(pesp_conn == NULL) + return; + lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse; + if(nud == NULL) + return; + if(nud->cb_disconnect_ref != LUA_NOREF && nud->self_ref != LUA_NOREF) + { + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_disconnect_ref); + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(client) to callback func in lua + lua_call(gL, 1, 0); + } + + if(pesp_conn->proto.tcp) + c_free(pesp_conn->proto.tcp); + pesp_conn->proto.tcp = NULL; + if(nud->pesp_conn) + c_free(nud->pesp_conn); + nud->pesp_conn = NULL; // espconn is already disconnected + lua_gc(gL, LUA_GCSTOP, 0); + if(nud->self_ref != LUA_NOREF){ + luaL_unref(gL, LUA_REGISTRYINDEX, nud->self_ref); + nud->self_ref = LUA_NOREF; // unref this, and the net.socket userdata will delete it self + } + lua_gc(gL, LUA_GCRESTART, 0); +} + +static void ICACHE_FLASH_ATTR +net_server_reconnected(void *arg, sint8_t err) +{ + NODE_DBG("net_server_reconnected is called.\n"); + net_server_disconnected(arg); +} + +static void ICACHE_FLASH_ATTR +net_socket_reconnected(void *arg, sint8_t err) +{ + NODE_DBG("net_socket_reconnected is called.\n"); + net_socket_disconnected(arg); +} + +static void ICACHE_FLASH_ATTR +net_socket_received(void *arg, char *pdata, unsigned short len) +{ + NODE_DBG("net_socket_received is called.\n"); + struct espconn *pesp_conn = arg; + if(pesp_conn == NULL) + return; + lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse; + if(nud == NULL) + return; + if(nud->cb_receive_ref == LUA_NOREF) + return; + if(nud->self_ref == LUA_NOREF) + return; + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_receive_ref); + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(server) to callback func in lua + // expose_array(gL, pdata, len); + // *(pdata+len) = 0; + // NODE_DBG(pdata); + // NODE_DBG("\n"); + lua_pushlstring(gL, pdata, len); + // lua_pushinteger(gL, len); + lua_call(gL, 2, 0); +} + +static void ICACHE_FLASH_ATTR +net_socket_sent(void *arg) +{ + // NODE_DBG("net_socket_sent is called.\n"); + struct espconn *pesp_conn = arg; + if(pesp_conn == NULL) + return; + lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse; + if(nud == NULL) + return; + if(nud->cb_send_ref == LUA_NOREF) + return; + if(nud->self_ref == LUA_NOREF) + return; + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_send_ref); + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(server) to callback func in lua + lua_call(gL, 1, 0); +} + +static void ICACHE_FLASH_ATTR +net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) +{ + NODE_DBG("net_dns_found is called.\n"); + struct espconn *pesp_conn = arg; + if(pesp_conn == NULL){ + NODE_DBG("pesp_conn null.\n"); + return; + } + lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse; + if(nud == NULL){ + NODE_DBG("nud null.\n"); + return; + } + if(nud->cb_dns_found_ref == LUA_NOREF){ + NODE_DBG("cb_dns_found_ref null.\n"); + return; + } + + if(ipaddr == NULL) + { + NODE_ERR( "DNS Fail!\n" ); + return; + } + + // ipaddr->addr is a uint32_t ip + char ip_str[20]; + c_memset(ip_str, 0, sizeof(ip_str)); + if(host_ip.addr == 0 && ipaddr->addr != 0) + { + c_sprintf(ip_str, IPSTR, IP2STR(&(ipaddr->addr))); + } + + if(nud->self_ref == LUA_NOREF){ + NODE_DBG("self_ref null.\n"); + return; + } + + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); // the callback function + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(conn) to callback func in lua + lua_pushstring(gL, ip_str); // the ip para + lua_call(gL, 2, 0); + + if((pesp_conn->type == ESPCONN_TCP && pesp_conn->proto.tcp->remote_port == 0) + || (pesp_conn->type == ESPCONN_UDP && pesp_conn->proto.udp->remote_port == 0) ){ + lua_gc(gL, LUA_GCSTOP, 0); + if(nud->self_ref != LUA_NOREF){ + luaL_unref(gL, LUA_REGISTRYINDEX, nud->self_ref); + nud->self_ref = LUA_NOREF; // unref this, and the net.socket userdata will delete it self + } + lua_gc(gL, LUA_GCRESTART, 0); + } +} + +static void ICACHE_FLASH_ATTR +net_server_connected(void *arg) // for tcp only +{ + NODE_DBG("net_server_connected is called.\n"); + struct espconn *pesp_conn = arg; + int i = 0; + lnet_userdata *skt = NULL; + if(pesp_conn == NULL) + return; + +#if 0 + char temp[20] = {0}; + c_sprintf(temp, IPSTR, IP2STR( &(pesp_conn->proto.tcp->remote_ip) ) ); + NODE_DBG("remote "); + NODE_DBG(temp); + NODE_DBG(":"); + NODE_DBG("%d",pesp_conn->proto.tcp->remote_port); + NODE_DBG(" connected.\n"); +#endif + + for(i=0;i=MAX_SOCKET) // can't create more socket + { + NODE_ERR("MAX_SOCKET\n"); + pesp_conn->reverse = NULL; // not accept this conn + if(pesp_conn->proto.tcp->remote_port || pesp_conn->proto.tcp->local_port) + espconn_disconnect(pesp_conn); + return; + } + + if(tcpserver_cb_connect_ref == LUA_NOREF) + return; + if(!gL) + return; + + lua_rawgeti(gL, LUA_REGISTRYINDEX, tcpserver_cb_connect_ref); // get function + // create a new client object + skt = (lnet_userdata *)lua_newuserdata(gL, sizeof(lnet_userdata)); + + if(!skt){ + NODE_ERR("can't newudata\n"); + lua_pop(gL, 1); + return; + } + // set its metatable + luaL_getmetatable(gL, "net.socket"); + lua_setmetatable(gL, -2); + // pre-initialize it, in case of errors + skt->self_ref = LUA_NOREF; + lua_pushvalue(gL, -1); // copy the top of stack + skt->self_ref = luaL_ref(gL, LUA_REGISTRYINDEX); // ref to it self, for module api to find the userdata + socket[i] = skt->self_ref; // save to socket array + socket_num++; + skt->cb_connect_ref = LUA_NOREF; // this socket already connected + skt->cb_reconnect_ref = LUA_NOREF; + skt->cb_disconnect_ref = LUA_NOREF; + + skt->cb_receive_ref = LUA_NOREF; + skt->cb_send_ref = LUA_NOREF; + skt->cb_dns_found_ref = LUA_NOREF; + +#ifdef CLIENT_SSL_ENABLE + skt->secure = 0; // as a server SSL is not supported. +#endif + + skt->pesp_conn = pesp_conn; // point to the espconn made by low level sdk + pesp_conn->reverse = skt; // let espcon carray the info of this userdata(net.socket) + + espconn_regist_recvcb(pesp_conn, net_socket_received); + espconn_regist_sentcb(pesp_conn, net_socket_sent); + espconn_regist_disconcb(pesp_conn, net_server_disconnected); + espconn_regist_reconcb(pesp_conn, net_server_reconnected); + + // now socket[i] has the client ref, and stack top has the userdata + lua_call(gL, 1, 0); // function(conn) +} + +static void ICACHE_FLASH_ATTR +net_socket_connected(void *arg) +{ + NODE_DBG("net_socket_connected is called.\n"); + struct espconn *pesp_conn = arg; + if(pesp_conn == NULL) + return; + lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse; + if(nud == NULL) + return; + // can receive and send data, even if there is no connected callback in lua. + espconn_regist_recvcb(pesp_conn, net_socket_received); + espconn_regist_sentcb(pesp_conn, net_socket_sent); + espconn_regist_disconcb(pesp_conn, net_socket_disconnected); + + if(nud->cb_connect_ref == LUA_NOREF) + return; + if(nud->self_ref == LUA_NOREF) + return; + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_connect_ref); + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(client) to callback func in lua + lua_call(gL, 1, 0); +} + +// Lua: s = net.create(type, secure/timeout, function(conn)) +static int ICACHE_FLASH_ATTR +net_create( lua_State* L, const char* mt ) +{ + NODE_DBG("net_create is called.\n"); + struct espconn *pesp_conn = NULL; + lnet_userdata *nud, *temp = NULL; + unsigned type; +#ifdef CLIENT_SSL_ENABLE + unsigned secure = 0; +#endif + uint8_t stack = 1; + bool isserver = false; + + if (mt!=NULL && c_strcmp(mt, "net.server")==0) + isserver = true; + else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) + isserver = false; + else + { + NODE_DBG("wrong metatable for net_create.\n"); + return 0; + } + + type = luaL_checkinteger( L, stack ); + if ( type != ESPCONN_TCP && type != ESPCONN_UDP ) + return luaL_error( L, "wrong arg type" ); + stack++; +#ifdef CLIENT_SSL_ENABLE + if(!isserver){ + if ( lua_isnumber(L, stack) ) + { + secure = lua_tointeger(L, stack); + stack++; + if ( secure != 0 && secure != 1 ){ + return luaL_error( L, "wrong arg type" ); + } + } else { + secure = 0; // default to 0 + } + } +#endif + + if(isserver && type == ESPCONN_TCP){ + if ( lua_isnumber(L, stack) ) + { + unsigned to = lua_tointeger(L, stack); + stack++; + if ( to < 1 || to > 28800 ){ + return luaL_error( L, "wrong arg type" ); + } + tcp_server_timeover = (uint16_t)to; + } else { + tcp_server_timeover = 30; // default to 30 + } + } + + // create a object + nud = (lnet_userdata *)lua_newuserdata(L, sizeof(lnet_userdata)); + // pre-initialize it, in case of errors + nud->self_ref = LUA_NOREF; + nud->cb_connect_ref = LUA_NOREF; + nud->cb_reconnect_ref = LUA_NOREF; + nud->cb_disconnect_ref = LUA_NOREF; + nud->cb_receive_ref = LUA_NOREF; + nud->cb_send_ref = LUA_NOREF; + nud->cb_dns_found_ref = LUA_NOREF; + nud->pesp_conn = NULL; +#ifdef CLIENT_SSL_ENABLE + nud->secure = secure; +#endif + + // set its metatable + luaL_getmetatable(L, mt); + lua_setmetatable(L, -2); + + // create the espconn struct + if(isserver && type==ESPCONN_TCP && pTcpServer){ + if(tcpserver_cb_connect_ref != LUA_NOREF){ // self_ref should be unref in close() + lua_pop(L,1); + return luaL_error(L, "only one tcp server allowed"); + } + pesp_conn = nud->pesp_conn = pTcpServer; + } else if(isserver && type==ESPCONN_UDP && pUdpServer){ + temp = (lnet_userdata *)pUdpServer->reverse; + if(temp && temp->self_ref != LUA_NOREF){ + lua_pop(L,1); + return luaL_error(L, "only one udp server allowed"); + } + pesp_conn = nud->pesp_conn = pUdpServer; + } else { + pesp_conn = nud->pesp_conn = (struct espconn *)c_zalloc(sizeof(struct espconn)); + if(!pesp_conn) + return luaL_error(L, "not enough memory"); + + pesp_conn->proto.tcp = NULL; + pesp_conn->proto.udp = NULL; + pesp_conn->reverse = NULL; + if( type==ESPCONN_TCP ) + { + pesp_conn->proto.tcp = (esp_tcp *)c_zalloc(sizeof(esp_tcp)); + if(!pesp_conn->proto.tcp){ + c_free(pesp_conn); + pesp_conn = nud->pesp_conn = NULL; + return luaL_error(L, "not enough memory"); + } + NODE_DBG("TCP server/socket is set.\n"); + } + else if( type==ESPCONN_UDP ) + { + pesp_conn->proto.udp = (esp_udp *)c_zalloc(sizeof(esp_udp)); + if(!pesp_conn->proto.udp){ + c_free(pesp_conn); + pesp_conn = nud->pesp_conn = NULL; + return luaL_error(L, "not enough memory"); + } + NODE_DBG("UDP server/socket is set.\n"); + } + } + pesp_conn->type = type; + pesp_conn->state = ESPCONN_NONE; + // reverse is for the callback function + pesp_conn->reverse = nud; + + if(isserver && type==ESPCONN_TCP && pTcpServer==NULL){ + pTcpServer = pesp_conn; + } else if(isserver && type==ESPCONN_UDP && pUdpServer==NULL){ + pUdpServer = pesp_conn; + } + + gL = L; // global L for net module. + + // if call back function is specified, call it with para userdata + // luaL_checkanyfunction(L, 2); + if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){ + lua_pushvalue(L, stack); // copy argument (func) to the top of stack + lua_pushvalue(L, -2); // copy the self_ref(userdata) to the top + lua_call(L, 1, 0); + } + + return 1; +} + +// static int net_close( lua_State* L, const char* mt ); +// Lua: net.delete( socket/server ) +// call close() first +// server: disconnect server, unref everything +// socket: unref everything +static int ICACHE_FLASH_ATTR +net_delete( lua_State* L, const char* mt ) +{ + NODE_DBG("net_delete is called.\n"); + bool isserver = false; + if (mt!=NULL && c_strcmp(mt, "net.server")==0) + isserver = true; + else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) + isserver = false; + else + { + NODE_DBG("wrong metatable for net_delete.\n"); + return 0; + } + + // net_close( L, mt ); // close it first + + lnet_userdata *nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); + luaL_argcheck(L, nud, 1, "Server/Socket expected"); + if(nud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + if(nud->pesp_conn){ // for client connected to tcp server, this should set NULL in disconnect cb + nud->pesp_conn->reverse = NULL; + if(!isserver) // socket is freed here + { + if(nud->pesp_conn->type == ESPCONN_UDP){ + if(nud->pesp_conn->proto.udp) + c_free(nud->pesp_conn->proto.udp); + nud->pesp_conn->proto.udp = NULL; + } else if (nud->pesp_conn->type == ESPCONN_TCP) { + if(nud->pesp_conn->proto.tcp) + c_free(nud->pesp_conn->proto.tcp); + nud->pesp_conn->proto.tcp = NULL; + } + c_free(nud->pesp_conn); + } + nud->pesp_conn = NULL; // for socket, it will free this when disconnected + } + + // free (unref) callback ref + if(LUA_NOREF!=nud->cb_connect_ref){ + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_connect_ref); + nud->cb_connect_ref = LUA_NOREF; + } + if(LUA_NOREF!=nud->cb_reconnect_ref){ + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_reconnect_ref); + nud->cb_reconnect_ref = LUA_NOREF; + } + if(LUA_NOREF!=nud->cb_disconnect_ref){ + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_disconnect_ref); + nud->cb_disconnect_ref = LUA_NOREF; + } + if(LUA_NOREF!=nud->cb_receive_ref){ + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_receive_ref); + nud->cb_receive_ref = LUA_NOREF; + } + if(LUA_NOREF!=nud->cb_send_ref){ + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_send_ref); + nud->cb_send_ref = LUA_NOREF; + } + if(LUA_NOREF!=nud->cb_dns_found_ref){ + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); + nud->cb_dns_found_ref = LUA_NOREF; + } + lua_gc(gL, LUA_GCSTOP, 0); + if(LUA_NOREF!=nud->self_ref){ + luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref); + nud->self_ref = LUA_NOREF; + } + lua_gc(gL, LUA_GCRESTART, 0); + return 0; +} + +static void ICACHE_FLASH_ATTR socket_connect(struct espconn *pesp_conn) +{ + if(pesp_conn == NULL) + return; + lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse; + if(nud == NULL) + return; + + if( pesp_conn->type == ESPCONN_TCP ) + { +#ifdef CLIENT_SSL_ENABLE + if(nud->secure){ + espconn_secure_connect(pesp_conn); + } + else +#endif + { + espconn_connect(pesp_conn); + } + } + else if (pesp_conn->type == ESPCONN_UDP) + { + espconn_create(pesp_conn); + } + NODE_DBG("socket_connect is called.\n"); +} + +static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg); +static dns_reconn_count = 0; +static void ICACHE_FLASH_ATTR +socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) +{ + NODE_DBG("socket_dns_found is called.\n"); + struct espconn *pesp_conn = arg; + if(pesp_conn == NULL){ + NODE_DBG("pesp_conn null.\n"); + return; + } + + if(ipaddr == NULL) + { + dns_reconn_count++; + if( dns_reconn_count >= 5 ){ + NODE_ERR( "DNS Fail!\n" ); + return; + } + NODE_ERR( "DNS retry %d!\n", dns_reconn_count ); + host_ip.addr = 0; + espconn_gethostbyname(pesp_conn, name, &host_ip, socket_dns_found); + return; + } + + // ipaddr->addr is a uint32_t ip + if(ipaddr->addr != 0) + { + dns_reconn_count = 0; + if( pesp_conn->type == ESPCONN_TCP ) + { + c_memcpy(pesp_conn->proto.tcp->remote_ip, &(ipaddr->addr), 4); + NODE_DBG("TCP ip is set: "); + NODE_DBG(IPSTR, IP2STR(&(ipaddr->addr))); + NODE_DBG("\n"); + } + else if (pesp_conn->type == ESPCONN_UDP) + { + c_memcpy(pesp_conn->proto.udp->remote_ip, &(ipaddr->addr), 4); + NODE_DBG("UDP ip is set: "); + NODE_DBG(IPSTR, IP2STR(&(ipaddr->addr))); + NODE_DBG("\n"); + } + socket_connect(pesp_conn); + } +} + +// Lua: server:listen( port, ip, function(con) ) +// Lua: socket:connect( port, ip, function(con) ) +static int ICACHE_FLASH_ATTR +net_start( lua_State* L, const char* mt ) +{ + NODE_DBG("net_start is called.\n"); + struct espconn *pesp_conn = NULL; + lnet_userdata *nud; + unsigned port; + size_t il; + bool isserver = false; + ip_addr_t ipaddr; + const char *domain; + uint8_t stack = 1; + + if (mt!=NULL && c_strcmp(mt, "net.server")==0) + isserver = true; + else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) + isserver = false; + else + { + NODE_DBG("wrong metatable for net_start.\n"); + return 0; + } + nud = (lnet_userdata *)luaL_checkudata(L, stack, mt); + luaL_argcheck(L, nud, stack, "Server/Socket expected"); + stack++; + + if(nud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + pesp_conn = nud->pesp_conn; + port = luaL_checkinteger( L, stack ); + stack++; + if( pesp_conn->type == ESPCONN_TCP ) + { + if(isserver) + pesp_conn->proto.tcp->local_port = port; + else + pesp_conn->proto.tcp->remote_port = port; + NODE_DBG("TCP port is set: %d.\n", port); + } + else if (pesp_conn->type == ESPCONN_UDP) + { + if(isserver) + pesp_conn->proto.udp->local_port = port; + else + pesp_conn->proto.udp->remote_port = port; + NODE_DBG("UDP port is set: %d.\n", port); + } + + if( lua_isstring(L,stack) ) // deal with the domain string + { + domain = luaL_checklstring( L, stack, &il ); + stack++; + if (domain == NULL) + { + if(isserver) + domain = "0.0.0.0"; + else + domain = "127.0.0.1"; + } + ipaddr.addr = ipaddr_addr(domain); + if( pesp_conn->type == ESPCONN_TCP ) + { + if(isserver) + c_memcpy(pesp_conn->proto.tcp->local_ip, &ipaddr.addr, 4); + else + c_memcpy(pesp_conn->proto.tcp->remote_ip, &ipaddr.addr, 4); + NODE_DBG("TCP ip is set: "); + NODE_DBG(IPSTR, IP2STR(&ipaddr.addr)); + NODE_DBG("\n"); + } + else if (pesp_conn->type == ESPCONN_UDP) + { + if(isserver) + c_memcpy(pesp_conn->proto.udp->local_ip, &ipaddr.addr, 4); + else + c_memcpy(pesp_conn->proto.udp->remote_ip, &ipaddr.addr, 4); + NODE_DBG("UDP ip is set: "); + NODE_DBG(IPSTR, IP2STR(&ipaddr.addr)); + NODE_DBG("\n"); + } + } + + // call back function when a connection is obtained, tcp only + if ( pesp_conn->type == ESPCONN_TCP ) { + if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){ + lua_pushvalue(L, stack); // copy argument (func) to the top of stack + if(isserver) // for tcp server connected callback + { + if(tcpserver_cb_connect_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, tcpserver_cb_connect_ref); + tcpserver_cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + else + { + if(nud->cb_connect_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_connect_ref); + nud->cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + } + } + + if(!isserver || pesp_conn->type == ESPCONN_UDP){ // self_ref is only needed by socket userdata, or udp server + lua_pushvalue(L, 1); // copy to the top of stack + if(nud->self_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref); + nud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + + if( pesp_conn->type == ESPCONN_TCP ) + { + if(isserver){ // no secure server support for now + espconn_regist_connectcb(pesp_conn, net_server_connected); + // tcp server, SSL is not supported +#ifdef CLIENT_SSL_ENABLE + // if(nud->secure) + // espconn_secure_accept(pesp_conn); + // else +#endif + espconn_accept(pesp_conn); // if it's a server, no need to dns. + espconn_regist_time(pesp_conn, tcp_server_timeover, 0); + } + else{ + espconn_regist_connectcb(pesp_conn, net_socket_connected); + espconn_regist_reconcb(pesp_conn, net_socket_reconnected); +#ifdef CLIENT_SSL_ENABLE + if(nud->secure){ + if(pesp_conn->proto.tcp->remote_port || pesp_conn->proto.tcp->local_port) + espconn_secure_disconnect(pesp_conn); + // espconn_secure_connect(pesp_conn); + } + else +#endif + { + if(pesp_conn->proto.tcp->remote_port || pesp_conn->proto.tcp->local_port) + espconn_disconnect(pesp_conn); + // espconn_connect(pesp_conn); + } + } + } + else if (pesp_conn->type == ESPCONN_UDP) + { + espconn_regist_recvcb(pesp_conn, net_socket_received); + espconn_regist_sentcb(pesp_conn, net_socket_sent); + if(pesp_conn->proto.tcp->remote_port || pesp_conn->proto.tcp->local_port) + espconn_delete(pesp_conn); + if(isserver) + espconn_create(pesp_conn); // if it's a server, no need to dns. + } + + if(!isserver){ + if((ipaddr.addr == IPADDR_NONE) && (c_memcmp(domain,"255.255.255.255",16) != 0)) + { + host_ip.addr = 0; + dns_reconn_count = 0; + if(ESPCONN_OK == espconn_gethostbyname(pesp_conn, domain, &host_ip, socket_dns_found)){ + socket_dns_found(domain, &host_ip, pesp_conn); // ip is returned in host_ip. + } + } + else + { + socket_connect(pesp_conn); + } + } + return 0; +} + +// Lua: server/socket:close() +// server disconnect everything, unref everything +// client disconnect and unref itself +static int ICACHE_FLASH_ATTR +net_close( lua_State* L, const char* mt ) +{ + NODE_DBG("net_close is called.\n"); + bool isserver = false; + int i = 0; + lnet_userdata *nud = NULL, *skt = NULL; + + nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); + luaL_argcheck(L, nud, 1, "Server/Socket expected"); + if(nud == NULL) + return 0; + + if(nud->pesp_conn == NULL) + return 0; + + if (mt!=NULL && c_strcmp(mt, "net.server")==0) + isserver = true; + else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) + isserver = false; + else + { + NODE_DBG("wrong metatable for net_close.\n"); + return 0; + } + + if(isserver && nud->pesp_conn->type == ESPCONN_TCP && tcpserver_cb_connect_ref != LUA_NOREF){ + luaL_unref(L, LUA_REGISTRYINDEX, tcpserver_cb_connect_ref); + tcpserver_cb_connect_ref = LUA_NOREF; + } + + int n = lua_gettop(L); + skt = nud; + + do{ + if(isserver && skt == NULL){ + if(socket[i] != LUA_NOREF){ // there is client socket exists + lua_rawgeti(L, LUA_REGISTRYINDEX, socket[i]); // get the referenced user_data to stack top +#if 0 + socket[i] = LUA_NOREF; + socket_num--; +#endif // do this in net_server_disconnected + i++; + if(lua_isuserdata(L,-1)){ + skt = lua_touserdata(L,-1); + } else { + lua_pop(L, 1); + continue; + } + }else{ + // skip LUA_NOREF + i++; + continue; + } + } + + if(skt==NULL){ + NODE_DBG("userdata is nil.\n"); + continue; + } + + if(skt->pesp_conn) // disconnect the connection + { + if(skt->pesp_conn->type == ESPCONN_TCP) + { + #ifdef CLIENT_SSL_ENABLE + if(skt->secure){ + if(skt->pesp_conn->proto.tcp->remote_port || skt->pesp_conn->proto.tcp->local_port) + espconn_secure_disconnect(skt->pesp_conn); + } + else + #endif + { + if(skt->pesp_conn->proto.tcp->remote_port || skt->pesp_conn->proto.tcp->local_port) + espconn_disconnect(skt->pesp_conn); + } + }else if(skt->pesp_conn->type == ESPCONN_UDP) + { + if(skt->pesp_conn->proto.tcp->remote_port || skt->pesp_conn->proto.tcp->local_port) + espconn_delete(skt->pesp_conn); + + // a udp server/socket unref it self here. not in disconnect. + if(LUA_NOREF!=skt->self_ref){ // for a udp self_ref is NOREF + luaL_unref(L, LUA_REGISTRYINDEX, skt->self_ref); + skt->self_ref = LUA_NOREF; // for a socket, now only var in lua is ref to the userdata + } + } + } +#if 0 + // unref the self_ref + if(LUA_NOREF!=skt->self_ref){ // for a server self_ref is NOREF + luaL_unref(L, LUA_REGISTRYINDEX, skt->self_ref); + skt->self_ref = LUA_NOREF; // for a socket, now only var in lua is ref to the userdata + } +#endif + lua_settop(L, n); // reset the stack top + skt = NULL; + } while( isserver && iself_ref){ // for a server self_ref is NOREF + luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref); + nud->self_ref = LUA_NOREF; // now only var in lua is ref to the userdata + } +#endif + + return 0; +} + +// Lua: socket/udpserver:on( "method", function(s) ) +static int ICACHE_FLASH_ATTR +net_on( lua_State* L, const char* mt ) +{ + NODE_DBG("net_on is called.\n"); + bool isserver = false; + lnet_userdata *nud; + size_t sl; + + nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); + luaL_argcheck(L, nud, 1, "Server/Socket expected"); + if(nud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + if (mt!=NULL && c_strcmp(mt, "net.server")==0) + isserver = true; + else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) + isserver = false; + else + { + NODE_DBG("wrong metatable for net_on.\n"); + return 0; + } + + const char *method = luaL_checklstring( L, 2, &sl ); + if (method == NULL) + return luaL_error( L, "wrong arg type" ); + + luaL_checkanyfunction(L, 3); + lua_pushvalue(L, 3); // copy argument (func) to the top of stack + + if(!isserver && nud->pesp_conn->type == ESPCONN_TCP && sl == 10 && c_strcmp(method, "connection") == 0){ + if(nud->cb_connect_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_connect_ref); + nud->cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX); + }else if(!isserver && nud->pesp_conn->type == ESPCONN_TCP && sl == 12 && c_strcmp(method, "reconnection") == 0){ + if(nud->cb_reconnect_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_reconnect_ref); + nud->cb_reconnect_ref = luaL_ref(L, LUA_REGISTRYINDEX); + }else if(!isserver && nud->pesp_conn->type == ESPCONN_TCP && sl == 13 && c_strcmp(method, "disconnection") == 0){ + if(nud->cb_disconnect_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_disconnect_ref); + nud->cb_disconnect_ref = luaL_ref(L, LUA_REGISTRYINDEX); + }else if((!isserver || nud->pesp_conn->type == ESPCONN_UDP) && sl == 7 && c_strcmp(method, "receive") == 0){ + if(nud->cb_receive_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_receive_ref); + nud->cb_receive_ref = luaL_ref(L, LUA_REGISTRYINDEX); + }else if((!isserver || nud->pesp_conn->type == ESPCONN_UDP) && sl == 4 && c_strcmp(method, "sent") == 0){ + if(nud->cb_send_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_send_ref); + nud->cb_send_ref = luaL_ref(L, LUA_REGISTRYINDEX); + }else if(!isserver && nud->pesp_conn->type == ESPCONN_TCP && sl == 3 && c_strcmp(method, "dns") == 0){ + if(nud->cb_dns_found_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); + nud->cb_dns_found_ref = luaL_ref(L, LUA_REGISTRYINDEX); + }else{ + lua_pop(L, 1); + return luaL_error( L, "method not supported" ); + } + + return 0; +} + +// Lua: server/socket:send( string, function(sent) ) +static int ICACHE_FLASH_ATTR +net_send( lua_State* L, const char* mt ) +{ + // NODE_DBG("net_send is called.\n"); + bool isserver = false; + struct espconn *pesp_conn = NULL; + lnet_userdata *nud; + size_t l; + + nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); + luaL_argcheck(L, nud, 1, "Server/Socket expected"); + if(nud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + if(nud->pesp_conn == NULL){ + NODE_DBG("nud->pesp_conn is NULL.\n"); + return 0; + } + pesp_conn = nud->pesp_conn; + + if (mt!=NULL && c_strcmp(mt, "net.server")==0) + isserver = true; + else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) + isserver = false; + else + { + NODE_DBG("wrong metatable for net_send.\n"); + return 0; + } + + if(isserver && nud->pesp_conn->type == ESPCONN_TCP){ + return luaL_error( L, "tcp server send not supported" ); + } + +#if 0 + char temp[20] = {0}; + c_sprintf(temp, IPSTR, IP2STR( &(pesp_conn->proto.tcp->remote_ip) ) ); + NODE_DBG("remote "); + NODE_DBG(temp); + NODE_DBG(":"); + NODE_DBG("%d",pesp_conn->proto.tcp->remote_port); + NODE_DBG(" sending data.\n"); +#endif + + const char *payload = luaL_checklstring( L, 2, &l ); + if (l>1460 || payload == NULL) + return luaL_error( L, "need <1460 payload" ); + + if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION){ + lua_pushvalue(L, 3); // copy argument (func) to the top of stack + if(nud->cb_send_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_send_ref); + nud->cb_send_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } +#ifdef CLIENT_SSL_ENABLE + if(nud->secure) + espconn_secure_sent(pesp_conn, (unsigned char *)payload, l); + else +#endif + espconn_sent(pesp_conn, (unsigned char *)payload, l); + + return 0; +} + +// Lua: socket:dns( string, function(socket, ip) ) +static int ICACHE_FLASH_ATTR +net_dns( lua_State* L, const char* mt ) +{ + NODE_DBG("net_dns is called.\n"); + bool isserver = false; + struct espconn *pesp_conn = NULL; + lnet_userdata *nud; + size_t l; + + nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); + luaL_argcheck(L, nud, 1, "Server/Socket expected"); + if(nud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + if (mt!=NULL && c_strcmp(mt, "net.server")==0) + isserver = true; + else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) + isserver = false; + else + { + NODE_DBG("wrong metatable for net_send.\n"); + return 0; + } + + pesp_conn = nud->pesp_conn; + + if(!isserver || pesp_conn->type == ESPCONN_UDP){ // self_ref is only needed by socket userdata, or udp server + lua_pushvalue(L, 1); // copy to the top of stack + if(nud->self_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref); + nud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + + const char *domain = luaL_checklstring( L, 2, &l ); + if (l>128 || domain == NULL) + return luaL_error( L, "need <128 domain" ); + + if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION){ + lua_pushvalue(L, 3); // copy argument (func) to the top of stack + if(nud->cb_dns_found_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); + nud->cb_dns_found_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + + host_ip.addr = 0; + espconn_gethostbyname(pesp_conn, domain, &host_ip, net_dns_found); + + return 0; +} + + +// Lua: s = net.createServer(type, function(server)) +static int ICACHE_FLASH_ATTR net_createServer( lua_State* L ) +{ + const char *mt = "net.server"; + return net_create(L, mt); +} + +// Lua: server:delete() +static int ICACHE_FLASH_ATTR +net_server_delete( lua_State* L ) +{ + const char *mt = "net.server"; + return net_delete(L, mt); +} + +// Lua: server:listen( port, ip ) +static int ICACHE_FLASH_ATTR +net_server_listen( lua_State* L ) +{ + const char *mt = "net.server"; + return net_start(L, mt); +} + +// Lua: server:close() +static int ICACHE_FLASH_ATTR +net_server_close( lua_State* L ) +{ + const char *mt = "net.server"; + return net_close(L, mt); +} + +// Lua: udpserver:on( "method", function(udpserver) ) +static int ICACHE_FLASH_ATTR +net_udpserver_on( lua_State* L ) +{ + const char *mt = "net.server"; + return net_on(L, mt); +} + +// Lua: udpserver:send(string, function() ) +static int ICACHE_FLASH_ATTR +net_udpserver_send( lua_State* L ) +{ + const char *mt = "net.server"; + return net_send(L, mt);; +} + +// Lua: s = net.createConnection(type, function(conn)) +static int ICACHE_FLASH_ATTR +net_createConnection( lua_State* L ) +{ + const char *mt = "net.socket"; + return net_create(L, mt); +} + +// Lua: socket:delete() +static int ICACHE_FLASH_ATTR +net_socket_delete( lua_State* L ) +{ + const char *mt = "net.socket"; + return net_delete(L, mt); +} + +// Lua: socket:connect( port, ip ) +static int ICACHE_FLASH_ATTR +net_socket_connect( lua_State* L ) +{ + const char *mt = "net.socket"; + return net_start(L, mt); +} + +// Lua: socket:close() +static int ICACHE_FLASH_ATTR +net_socket_close( lua_State* L ) +{ + const char *mt = "net.socket"; + return net_close(L, mt); +} + +// Lua: socket:on( "method", function(socket) ) +static int ICACHE_FLASH_ATTR +net_socket_on( lua_State* L ) +{ + const char *mt = "net.socket"; + return net_on(L, mt); +} + +// Lua: socket:send( string, function() ) +static int ICACHE_FLASH_ATTR +net_socket_send( lua_State* L ) +{ + const char *mt = "net.socket"; + return net_send(L, mt); +} + +// Lua: socket:dns( string, function(ip) ) +static int ICACHE_FLASH_ATTR +net_socket_dns( lua_State* L ) +{ + const char *mt = "net.socket"; + return net_dns(L, mt); +} + +#if 0 +static int ICACHE_FLASH_ATTR +net_array_index( lua_State* L ) +{ + char** parray = luaL_checkudata(L, 1, "net.array"); + int index = luaL_checkint(L, 2); + lua_pushnumber(L, (*parray)[index-1]); + return 1; +} + +static int ICACHE_FLASH_ATTR +net_array_newindex( lua_State* L ) +{ + char** parray = luaL_checkudata(L, 1, "net.array"); + int index = luaL_checkint(L, 2); + int value = luaL_checkint(L, 3); + (*parray)[index-1] = value; + return 0; +} + +// expose an array to lua, by storing it in a userdata with the array metatable +static int ICACHE_FLASH_ATTR +expose_array(lua_State* L, char *array, unsigned short len) { + char** parray = lua_newuserdata(L, len); + *parray = array; + luaL_getmetatable(L, "net.array"); + lua_setmetatable(L, -2); + return 1; +} +#endif + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +static const LUA_REG_TYPE net_server_map[] = +{ + { LSTRKEY( "listen" ), LFUNCVAL ( net_server_listen ) }, + { LSTRKEY( "close" ), LFUNCVAL ( net_server_close ) }, + { LSTRKEY( "on" ), LFUNCVAL ( net_udpserver_on ) }, + { LSTRKEY( "send" ), LFUNCVAL ( net_udpserver_send ) }, + // { LSTRKEY( "delete" ), LFUNCVAL ( net_server_delete ) }, + { LSTRKEY( "__gc" ), LFUNCVAL ( net_server_delete ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + { LSTRKEY( "__index" ), LROVAL ( net_server_map ) }, +#endif + { LNILKEY, LNILVAL } +}; + +static const LUA_REG_TYPE net_socket_map[] = +{ + { LSTRKEY( "connect" ), LFUNCVAL( net_socket_connect ) }, + { LSTRKEY( "close" ), LFUNCVAL ( net_socket_close ) }, + { LSTRKEY( "on" ), LFUNCVAL ( net_socket_on ) }, + { LSTRKEY( "send" ), LFUNCVAL ( net_socket_send ) }, + { LSTRKEY( "dns" ), LFUNCVAL ( net_socket_dns ) }, + // { LSTRKEY( "delete" ), LFUNCVAL ( net_socket_delete ) }, + { LSTRKEY( "__gc" ), LFUNCVAL ( net_socket_delete ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + { LSTRKEY( "__index" ), LROVAL ( net_socket_map ) }, +#endif + { LNILKEY, LNILVAL } +}; +#if 0 +static const LUA_REG_TYPE net_array_map[] = +{ + { LSTRKEY( "__index" ), LFUNCVAL( net_array_index ) }, + { LSTRKEY( "__newindex" ), LFUNCVAL( net_array_newindex ) }, + { LNILKEY, LNILVAL } +}; +#endif +const LUA_REG_TYPE net_map[] = +{ + { LSTRKEY( "createServer" ), LFUNCVAL ( net_createServer ) }, + { LSTRKEY( "createConnection" ), LFUNCVAL ( net_createConnection ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + { LSTRKEY( "TCP" ), LNUMVAL( TCP ) }, + { LSTRKEY( "UDP" ), LNUMVAL( UDP ) }, + + { LSTRKEY( "__metatable" ), LROVAL( net_map ) }, +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_net( lua_State *L ) +{ + int i; + for(i=0;i 0 + luaL_rometatable(L, "net.server", (void *)net_server_map); // create metatable for net.server + luaL_rometatable(L, "net.socket", (void *)net_socket_map); // create metatable for net.socket + #if 0 + luaL_rometatable(L, "net.array", (void *)net_array_map); // create metatable for net.array + #endif + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + int n; + luaL_register( L, AUXLIB_NET, net_map ); + + // Set it as its own metatable + lua_pushvalue( L, -1 ); + lua_setmetatable( L, -2 ); + + // Module constants + MOD_REG_NUMBER( L, "TCP", TCP ); + MOD_REG_NUMBER( L, "UDP", UDP ); + + n = lua_gettop(L); + + // create metatable + luaL_newmetatable(L, "net.server"); + // metatable.__index = metatable + lua_pushliteral(L, "__index"); + lua_pushvalue(L,-2); + lua_rawset(L,-3); + // Setup the methods inside metatable + luaL_register( L, NULL, net_server_map ); + + lua_settop(L, n); + // create metatable + luaL_newmetatable(L, "net.socket"); + // metatable.__index = metatable + lua_pushliteral(L, "__index"); + lua_pushvalue(L,-2); + lua_rawset(L,-3); + // Setup the methods inside metatable + luaL_register( L, NULL, net_socket_map ); +#if 0 + lua_settop(L, n); + // create metatable + luaL_newmetatable(L, "net.array"); + // Setup the methods inside metatable + luaL_register( L, NULL, net_array_map ); +#endif + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} diff --git a/app/modules/node.c b/app/modules/node.c new file mode 100644 index 00000000..94b2f7a1 --- /dev/null +++ b/app/modules/node.c @@ -0,0 +1,262 @@ +// Module for interfacing with system + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" + +#include "c_types.h" +#include "romfs.h" +#include "c_string.h" +#include "driver/uart.h" + +// Lua: restart() +static int ICACHE_FLASH_ATTR node_restart( lua_State* L ) +{ + system_restart(); + return 0; +} + +// Lua: dsleep( us ) +static int ICACHE_FLASH_ATTR node_deepsleep( lua_State* L ) +{ + s32 us; + us = luaL_checkinteger( L, 1 ); + if ( us <= 0 ) + return luaL_error( L, "wrong arg range" ); + system_deep_sleep( us ); + return 0; +} + +// Lua: chipid() +static int ICACHE_FLASH_ATTR node_chipid( lua_State* L ) +{ + uint32_t id = system_get_chip_id(); + lua_pushinteger(L, id); + return 1; +} + +// Lua: heap() +static int ICACHE_FLASH_ATTR node_heap( lua_State* L ) +{ + uint32_t sz = system_get_free_heap_size(); + lua_pushinteger(L, sz); + return 1; +} + +extern int led_high_count; // this is defined in lua.c +extern int led_low_count; +// Lua: led(low, high) +static int ICACHE_FLASH_ATTR node_led( lua_State* L ) +{ + int low, high; + if ( lua_isnumber(L, 1) ) + { + low = lua_tointeger(L, 1); + if ( low < 0 ){ + return luaL_error( L, "wrong arg type" ); + } + } else { + low = LED_LOW_COUNT_DEFAULT; // default to LED_LOW_COUNT_DEFAULT + } + if ( lua_isnumber(L, 2) ) + { + high = lua_tointeger(L, 2); + if ( high < 0 ){ + return luaL_error( L, "wrong arg type" ); + } + } else { + high = LED_HIGH_COUNT_DEFAULT; // default to LED_HIGH_COUNT_DEFAULT + } + led_high_count = (uint32_t)high / READLINE_INTERVAL; + led_low_count = (uint32_t)low / READLINE_INTERVAL; + return 0; +} + +static int long_key_ref = LUA_NOREF; +static int short_key_ref = LUA_NOREF; +static lua_State *gL = NULL; + +void ICACHE_FLASH_ATTR default_long_press(void *arg){ + if(led_high_count == 12 && led_low_count == 12){ + led_low_count = led_high_count = 6; + } else { + led_low_count = led_high_count = 12; + } + // led_high_count = 1000 / READLINE_INTERVAL; + // led_low_count = 1000 / READLINE_INTERVAL; + // NODE_DBG("default_long_press is called. hc: %d, lc: %d\n", led_high_count, led_low_count); +} + +void ICACHE_FLASH_ATTR default_short_press(void *arg){ + system_restart(); +} + +void ICACHE_FLASH_ATTR key_long_press(void *arg){ + NODE_DBG("key_long_press is called.\n"); + if(long_key_ref == LUA_NOREF){ + default_long_press(arg); + return; + } + if(!gL) + return; + lua_rawgeti(gL, LUA_REGISTRYINDEX, long_key_ref); + lua_call(gL, 0, 0); +} + +void ICACHE_FLASH_ATTR key_short_press(void *arg){ + NODE_DBG("key_short_press is called.\n"); + if(short_key_ref == LUA_NOREF){ + default_short_press(arg); + return; + } + if(!gL) + return; + lua_rawgeti(gL, LUA_REGISTRYINDEX, short_key_ref); + lua_call(gL, 0, 0); +} + +// Lua: key(type, function) +static int ICACHE_FLASH_ATTR node_key( lua_State* L ) +{ + int *ref = NULL; + size_t sl; + + const char *str = luaL_checklstring( L, 1, &sl ); + if (str == NULL) + return luaL_error( L, "wrong arg type" ); + + if(sl == 5 && c_strcmp(str, "short") == 0){ + ref = &short_key_ref; + }else if(sl == 4 && c_strcmp(str, "long") == 0){ + ref = &long_key_ref; + }else{ + ref = &short_key_ref; + } + gL = L; + // luaL_checkanyfunction(L, 2); + if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION){ + lua_pushvalue(L, 2); // copy argument (func) to the top of stack + if(*ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, *ref); + *ref = luaL_ref(L, LUA_REGISTRYINDEX); + } else { // unref the key press function + if(*ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, *ref); + *ref = LUA_NOREF; + } + + return 0; +} + +extern lua_Load gLoad; +extern os_timer_t lua_timer; +extern void dojob(lua_Load *load); +// Lua: input("string") +static int ICACHE_FLASH_ATTR node_input( lua_State* L ) +{ + size_t l=0; + const char *s = luaL_checklstring(L, 1, &l); + if (s != NULL && l > 0 && l < LUA_MAXINPUT - 1) + { + lua_Load *load = &gLoad; + if(load->line_position == 0){ + c_memcpy(load->line, s, l); + load->line[l+1] = '\0'; + load->line_position = c_strlen(load->line)+1; + load->done = 1; + NODE_DBG("Get command:\n"); + NODE_DBG(load->line); // buggy here + NODE_DBG("\nResult(if any):\n"); + os_timer_disarm(&lua_timer); + os_timer_setfn(&lua_timer, (os_timer_func_t *)dojob, load); + os_timer_arm(&lua_timer, READLINE_INTERVAL, 0); // no repeat + } + } + return 0; +} + +static int output_redir_ref = LUA_NOREF; +static int serial_debug = 1; +void ICACHE_FLASH_ATTR output_redirect(const char *str){ + // if(c_strlen(str)>=TX_BUFF_SIZE){ + // NODE_ERR("output too long.\n"); + // return; + // } + + if(output_redir_ref == LUA_NOREF || !gL){ + uart0_sendStr(str); + return; + } + + if(serial_debug!=0){ + uart0_sendStr(str); + } + + lua_rawgeti(gL, LUA_REGISTRYINDEX, output_redir_ref); + lua_pushstring(gL, str); + lua_call(gL, 1, 0); // this call back function should never user output. +} + +// Lua: output(function(c), debug) +static int ICACHE_FLASH_ATTR node_output( lua_State* L ) +{ + gL = L; + // luaL_checkanyfunction(L, 1); + if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION){ + lua_pushvalue(L, 1); // copy argument (func) to the top of stack + if(output_redir_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref); + output_redir_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } else { // unref the key press function + if(output_redir_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref); + output_redir_ref = LUA_NOREF; + serial_debug = 1; + return 0; + } + + if ( lua_isnumber(L, 2) ) + { + serial_debug = lua_tointeger(L, 2); + if(serial_debug!=0) + serial_debug = 1; + } else { + serial_debug = 1; // default to 1 + } + + return 0; +} + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE node_map[] = +{ + { LSTRKEY( "restart" ), LFUNCVAL( node_restart ) }, + { LSTRKEY( "dsleep" ), LFUNCVAL( node_deepsleep ) }, + { LSTRKEY( "chipid" ), LFUNCVAL( node_chipid ) }, + { LSTRKEY( "heap" ), LFUNCVAL( node_heap ) }, + { LSTRKEY( "key" ), LFUNCVAL( node_key ) }, + { LSTRKEY( "led" ), LFUNCVAL( node_led ) }, + { LSTRKEY( "input" ), LFUNCVAL( node_input ) }, + { LSTRKEY( "output" ), LFUNCVAL( node_output ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_node( lua_State *L ) +{ +#if LUA_OPTIMIZE_MEMORY > 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_NODE, node_map ); + // Add constants + + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} diff --git a/app/modules/ow.c b/app/modules/ow.c new file mode 100644 index 00000000..6a399752 --- /dev/null +++ b/app/modules/ow.c @@ -0,0 +1,327 @@ +// Module for interfacing with the OneWire interface + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "auxmods.h" +#include "lrotable.h" +#include "driver/onewire.h" + +// Lua: ow.setup( id ) +static int ICACHE_FLASH_ATTR ow_setup( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + + if(id==0) + return luaL_error( L, "no 1-wire for D0" ); + + MOD_CHECK_ID( ow, id ); + + onewire_init( id ); + return 0; +} + +// Lua: r = ow.reset( id ) +static int ICACHE_FLASH_ATTR ow_reset( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( ow, id ); + lua_pushinteger( L, onewire_reset(id) ); + return 1; +} + +// Lua: ow.skip( id ) +static int ICACHE_FLASH_ATTR ow_skip( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( ow, id ); + onewire_skip(id); + return 0; +} + +// Lua: ow.select( id, buf[8]) +static int ICACHE_FLASH_ATTR ow_select( lua_State *L ) +{ + uint8_t rom[8]; + size_t datalen; + int numdata, i; + unsigned id = luaL_checkinteger( L, 1 ); + const char *pdata; + MOD_CHECK_ID( ow, id ); + + if( lua_istable( L, 2 ) ) + { + datalen = lua_objlen( L, 2 ); + if (datalen!=8) + return luaL_error( L, "wrong arg range" ); + for( i = 0; i < datalen; i ++ ) + { + lua_rawgeti( L, 2, i + 1 ); + numdata = ( int )luaL_checkinteger( L, -1 ); + lua_pop( L, 1 ); + if( numdata > 255 ) + return luaL_error( L, "wrong arg range" ); + rom[i] = (uint8_t)numdata; + } + } + else + { + pdata = luaL_checklstring( L, 2, &datalen ); + if (datalen!=8) + return luaL_error( L, "wrong arg range" ); + for( i = 0; i < datalen; i ++ ){ + rom[i] = pdata[i]; + } + } + + onewire_select(id, rom); + return 0; +} + +// Lua: ow.write( id, v, power) +static int ICACHE_FLASH_ATTR ow_write( lua_State *L ) +{ + int power = 0; + unsigned id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( ow, id ); + + int v = (int)luaL_checkinteger( L, 2 ); + if( v > 255 ) + return luaL_error( L, "wrong arg range" ); + if(lua_isnumber(L, 3)) + power = lua_tointeger(L, 3); + if(power!=0) + power = 1; + + onewire_write((uint8_t)id, (uint8_t)v, (uint8_t)power); + + return 0; +} + +// Lua: ow.write_bytes( id, buf, power) +static int ICACHE_FLASH_ATTR ow_write_bytes( lua_State *L ) +{ + int power = 0; + size_t datalen; + unsigned id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( ow, id ); + + const uint8_t *pdata = luaL_checklstring( L, 2, &datalen ); + + if(lua_isnumber(L, 3)) + power = lua_tointeger(L, 3); + if(power!=0) + power = 1; + + onewire_write_bytes((uint8_t)id, pdata, (uint16_t)datalen, (uint8_t)power); + + return 0; +} + +// Lua: r = ow.read( id ) +static int ICACHE_FLASH_ATTR ow_read( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( ow, id ); + lua_pushinteger( L, onewire_read(id) ); + return 1; +} + +// Lua: r = ow.read_bytes( id, size ) +static int ICACHE_FLASH_ATTR ow_read_bytes( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( ow, id ); + u32 size = ( u32 )luaL_checkinteger( L, 2 ); + if( size == 0 ) + return 0; + + luaL_Buffer b; + luaL_buffinit( L, &b ); + char *p = luaL_prepbuffer(&b); + + onewire_read_bytes(id, (uint8_t *)p, size); + + luaL_addsize(&b, size); + luaL_pushresult( &b ); + return 1; +} + +// Lua: ow.depower( id ) +static int ICACHE_FLASH_ATTR ow_depower( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( ow, id ); + onewire_depower(id); + return 0; +} + +#if ONEWIRE_SEARCH +// Clear the search state so that if will start from the beginning again. +// Lua: ow.reset_search( id ) +static int ICACHE_FLASH_ATTR ow_reset_search( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( ow, id ); + onewire_reset_search(id); + return 0; +} + + +// Setup the search to find the device type 'family_code' on the next call +// to search(*newAddr) if it is present. +// Lua: ow.target_search( id, family_code) +static int ICACHE_FLASH_ATTR ow_target_search( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( ow, id ); + + int code = (int)luaL_checkinteger( L, 2 ); + if( code > 255 ) + return luaL_error( L, "wrong arg range" ); + + onewire_target_search((uint8_t)id, (uint8_t)code); + + return 0; +} + +// Look for the next device. Returns 1 if a new address has been +// returned. A zero might mean that the bus is shorted, there are +// no devices, or you have already retrieved all of them. It +// might be a good idea to check the CRC to make sure you didn't +// get garbage. The order is deterministic. You will always get +// the same devices in the same order. + +// Lua: r = ow.search( id ) +static int ICACHE_FLASH_ATTR ow_search( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( ow, id ); + + luaL_Buffer b; + luaL_buffinit( L, &b ); + char *p = luaL_prepbuffer(&b); + + if(onewire_search(id, (uint8_t *)p)){ + luaL_addsize(&b, 8); + luaL_pushresult( &b ); + } else { + luaL_pushresult(&b); /* close buffer */ + lua_pop(L,1); + lua_pushnil(L); + } + return 1; +} +#endif + +#if ONEWIRE_CRC +// uint8_t onewire_crc8(const uint8_t *addr, uint8_t len); +// Lua: r = ow.crc8( buf ) +static int ICACHE_FLASH_ATTR ow_crc8( lua_State *L ) +{ + size_t datalen; + const uint8_t *pdata = luaL_checklstring( L, 1, &datalen ); + if(datalen > 255) + return luaL_error( L, "wrong arg range" ); + lua_pushinteger( L, onewire_crc8(pdata, (uint8_t)datalen) ); + return 1; +} + +#if ONEWIRE_CRC16 +// bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc); +// Lua: b = ow.check_crc16( buf, inverted_crc0, inverted_crc1, crc ) +static int ICACHE_FLASH_ATTR ow_check_crc16( lua_State *L ) +{ + size_t datalen; + uint8_t inverted_crc[2]; + const uint8_t *pdata = luaL_checklstring( L, 1, &datalen ); + if(datalen > 65535) + return luaL_error( L, "wrong arg range" ); + + int crc = 0; + crc = luaL_checkinteger( L, 2 ); + if(datalen > 255) + return luaL_error( L, "wrong arg range" ); + inverted_crc[0] = (uint8_t)crc; + + crc = luaL_checkinteger( L, 3 ); + if(datalen > 255) + return luaL_error( L, "wrong arg range" ); + inverted_crc[1] = (uint8_t)crc; + + crc = 0; + if(lua_isnumber(L, 4)) + crc = lua_tointeger(L, 4); + if(crc > 65535) + return luaL_error( L, "wrong arg range" ); + + lua_pushboolean( L, onewire_check_crc16(pdata, (uint16_t)datalen, inverted_crc, (uint16_t)crc) ); + + return 1; +} + +// uint16_t onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc); +// Lua: r = ow.crc16( buf, crc ) +static int ICACHE_FLASH_ATTR ow_crc16( lua_State *L ) +{ + size_t datalen; + const uint8_t *pdata = luaL_checklstring( L, 1, &datalen ); + if(datalen > 65535) + return luaL_error( L, "wrong arg range" ); + int crc = 0; + if(lua_isnumber(L, 2)) + crc = lua_tointeger(L, 2); + if(crc > 65535) + return luaL_error( L, "wrong arg range" ); + + lua_pushinteger( L, onewire_crc16(pdata, (uint16_t)datalen, (uint16_t)crc) ); + + return 1; +} +#endif +#endif + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE ow_map[] = +{ + { LSTRKEY( "setup" ), LFUNCVAL( ow_setup ) }, + { LSTRKEY( "reset" ), LFUNCVAL( ow_reset ) }, + { LSTRKEY( "skip" ), LFUNCVAL( ow_skip ) }, + { LSTRKEY( "select" ), LFUNCVAL( ow_select ) }, + { LSTRKEY( "write" ), LFUNCVAL( ow_write ) }, + { LSTRKEY( "write_bytes" ), LFUNCVAL( ow_write_bytes ) }, + { LSTRKEY( "read" ), LFUNCVAL( ow_read ) }, + { LSTRKEY( "read_bytes" ), LFUNCVAL( ow_read_bytes ) }, + { LSTRKEY( "depower" ), LFUNCVAL( ow_depower ) }, +#if ONEWIRE_SEARCH + { LSTRKEY( "reset_search" ), LFUNCVAL( ow_reset_search ) }, + { LSTRKEY( "target_search" ), LFUNCVAL( ow_target_search ) }, + { LSTRKEY( "search" ), LFUNCVAL( ow_search ) }, +#endif +#if ONEWIRE_CRC + { LSTRKEY( "crc8" ), LFUNCVAL( ow_crc8 ) }, +#if ONEWIRE_CRC16 + { LSTRKEY( "check_crc16" ), LFUNCVAL( ow_check_crc16 ) }, + { LSTRKEY( "crc16" ), LFUNCVAL( ow_crc16 ) }, +#endif +#endif +#if LUA_OPTIMIZE_MEMORY > 0 + +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_ow( lua_State *L ) +{ +#if LUA_OPTIMIZE_MEMORY > 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_OW, ow_map ); + + // Add the constants + + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} diff --git a/app/modules/pwm.c b/app/modules/pwm.c new file mode 100644 index 00000000..ead3f064 --- /dev/null +++ b/app/modules/pwm.c @@ -0,0 +1,154 @@ +// Module for interfacing with PWM + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" + +#include "c_types.h" + +// Lua: realfrequency = setup( id, frequency, duty ) +static int ICACHE_FLASH_ATTR lpwm_setup( lua_State* L ) +{ + s32 freq; // signed, to error check for negative values + unsigned duty; + unsigned id; + + id = luaL_checkinteger( L, 1 ); + if(id==0) + return luaL_error( L, "no pwm for D0" ); + MOD_CHECK_ID( pwm, id ); + freq = luaL_checkinteger( L, 2 ); + if ( freq <= 0 ) + return luaL_error( L, "wrong arg range" ); + duty = luaL_checkinteger( L, 3 ); + if ( duty > NORMAL_PWM_DEPTH ) + // Negative values will turn out > 100, so will also fail. + return luaL_error( L, "wrong arg range" ); + freq = platform_pwm_setup( id, (u32)freq, duty ); + if(freq==0) + return luaL_error( L, "too many pwms." ); + lua_pushinteger( L, freq ); + return 1; +} + +// Lua: close( id ) +static int ICACHE_FLASH_ATTR lpwm_close( lua_State* L ) +{ + unsigned id; + + id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( pwm, id ); + platform_pwm_close( id ); + return 0; +} + +// Lua: start( id ) +static int ICACHE_FLASH_ATTR lpwm_start( lua_State* L ) +{ + unsigned id; + id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( pwm, id ); + platform_pwm_start( id ); + return 0; +} + +// Lua: stop( id ) +static int ICACHE_FLASH_ATTR lpwm_stop( lua_State* L ) +{ + unsigned id; + + id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( pwm, id ); + platform_pwm_stop( id ); + return 0; +} + +// Lua: realclock = setclock( id, clock ) +static int ICACHE_FLASH_ATTR lpwm_setclock( lua_State* L ) +{ + unsigned id; + s32 clk; // signed to error-check for negative values + + id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( pwm, id ); + clk = luaL_checkinteger( L, 2 ); + if ( clk <= 0 ) + return luaL_error( L, "wrong arg range" ); + clk = platform_pwm_set_clock( id, (u32)clk ); + lua_pushinteger( L, clk ); + return 1; +} + +// Lua: clock = getclock( id ) +static int ICACHE_FLASH_ATTR lpwm_getclock( lua_State* L ) +{ + unsigned id; + u32 clk; + + id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( pwm, id ); + clk = platform_pwm_get_clock( id ); + lua_pushinteger( L, clk ); + return 1; +} + +// Lua: realduty = setduty( id, duty ) +static int ICACHE_FLASH_ATTR lpwm_setduty( lua_State* L ) +{ + unsigned id; + s32 duty; // signed to error-check for negative values + + id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( pwm, id ); + duty = luaL_checkinteger( L, 2 ); + if ( duty > NORMAL_PWM_DEPTH ) + return luaL_error( L, "wrong arg range" ); + duty = platform_pwm_set_duty( id, (u32)duty ); + lua_pushinteger( L, duty ); + return 1; +} + +// Lua: duty = getduty( id ) +static int ICACHE_FLASH_ATTR lpwm_getduty( lua_State* L ) +{ + unsigned id; + u32 duty; + + id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( pwm, id ); + duty = platform_pwm_get_duty( id ); + lua_pushinteger( L, duty ); + return 1; +} + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE pwm_map[] = +{ + { LSTRKEY( "setup" ), LFUNCVAL( lpwm_setup ) }, + { LSTRKEY( "close" ), LFUNCVAL( lpwm_close ) }, + { LSTRKEY( "start" ), LFUNCVAL( lpwm_start ) }, + { LSTRKEY( "stop" ), LFUNCVAL( lpwm_stop ) }, + { LSTRKEY( "setclock" ), LFUNCVAL( lpwm_setclock ) }, + { LSTRKEY( "getclock" ), LFUNCVAL( lpwm_getclock ) }, + { LSTRKEY( "setduty" ), LFUNCVAL( lpwm_setduty ) }, + { LSTRKEY( "getduty" ), LFUNCVAL( lpwm_getduty ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_pwm( lua_State *L ) +{ +#if LUA_OPTIMIZE_MEMORY > 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_PWM, pwm_map ); + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} diff --git a/app/modules/tmr.c b/app/modules/tmr.c new file mode 100644 index 00000000..9c0e2d47 --- /dev/null +++ b/app/modules/tmr.c @@ -0,0 +1,189 @@ +// Module for interfacing with timer + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" + +#include "c_types.h" + +static os_timer_t alarm_timer[NUM_TMR]; +static int alarm_timer_cb_ref[NUM_TMR] = {LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF}; + +void ICACHE_FLASH_ATTR alarm_timer_common(lua_State* L, unsigned id){ + if(alarm_timer_cb_ref[id] == LUA_NOREF) + return; + lua_rawgeti(L, LUA_REGISTRYINDEX, alarm_timer_cb_ref[id]); + lua_call(L, 0, 0); +} + +void ICACHE_FLASH_ATTR +alarm_timer_cb0(void *arg){ + if( !arg ) + return; + alarm_timer_common((lua_State*)arg, 0); +} + +void ICACHE_FLASH_ATTR +alarm_timer_cb1(void *arg){ + if( !arg ) + return; + alarm_timer_common((lua_State*)arg, 1); +} + +void ICACHE_FLASH_ATTR +alarm_timer_cb2(void *arg){ + if( !arg ) + return; + alarm_timer_common((lua_State*)arg, 2); +} + +void ICACHE_FLASH_ATTR +alarm_timer_cb3(void *arg){ + if( !arg ) + return; + alarm_timer_common((lua_State*)arg, 3); +} + +void ICACHE_FLASH_ATTR +alarm_timer_cb4(void *arg){ + if( !arg ) + return; + alarm_timer_common((lua_State*)arg, 4); +} + +void ICACHE_FLASH_ATTR +alarm_timer_cb5(void *arg){ + if( !arg ) + return; + alarm_timer_common((lua_State*)arg, 5); +} + +void ICACHE_FLASH_ATTR +alarm_timer_cb6(void *arg){ + if( !arg ) + return; + alarm_timer_common((lua_State*)arg, 6); +} + +typedef void (*alarm_timer_callback)(void *arg); +static alarm_timer_callback alarm_timer_cb[NUM_TMR] = {alarm_timer_cb0,alarm_timer_cb1,alarm_timer_cb2,alarm_timer_cb3,alarm_timer_cb4,alarm_timer_cb5,alarm_timer_cb6}; + +// Lua: delay( us ) +static int ICACHE_FLASH_ATTR tmr_delay( lua_State* L ) +{ + s32 us; + us = luaL_checkinteger( L, 1 ); + if ( us <= 0 ) + return luaL_error( L, "wrong arg range" ); + unsigned sec = (unsigned)us / 1000000; + unsigned remain = (unsigned)us % 1000000; + int i = 0; + for(i=0;i0) + os_delay_us( remain ); + return 0; +} + +// Lua: now() , return system timer in us +static int ICACHE_FLASH_ATTR tmr_now( lua_State* L ) +{ + unsigned now = 0x7FFFFFFF & system_get_time(); + lua_pushinteger( L, now ); + return 1; +} + +// Lua: alarm( id, interval, repeat, function ) +static int ICACHE_FLASH_ATTR tmr_alarm( lua_State* L ) +{ + s32 interval; + unsigned repeat = 0; + int stack = 1; + + unsigned id = luaL_checkinteger( L, stack ); + stack++; + MOD_CHECK_ID( tmr, id ); + + interval = luaL_checkinteger( L, stack ); + stack++; + if ( interval <= 0 ) + return luaL_error( L, "wrong arg range" ); + + if ( lua_isnumber(L, stack) ){ + repeat = lua_tointeger(L, stack); + stack++; + if ( repeat != 1 && repeat != 0 ) + return luaL_error( L, "wrong arg type" ); + } + + // luaL_checkanyfunction(L, stack); + if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){ + lua_pushvalue(L, stack); // copy argument (func) to the top of stack + if(alarm_timer_cb_ref[id] != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, alarm_timer_cb_ref[id]); + alarm_timer_cb_ref[id] = luaL_ref(L, LUA_REGISTRYINDEX); + } + + os_timer_disarm(&(alarm_timer[id])); + os_timer_setfn(&(alarm_timer[id]), (os_timer_func_t *)(alarm_timer_cb[id]), L); + os_timer_arm(&(alarm_timer[id]), interval, repeat); + return 0; +} + +// Lua: stop( id ) +static int ICACHE_FLASH_ATTR tmr_stop( lua_State* L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( tmr, id ); + + os_timer_disarm(&(alarm_timer[id])); + return 0; +} + +// extern void update_key_led(); +// Lua: wdclr() +static int ICACHE_FLASH_ATTR tmr_wdclr( lua_State* L ) +{ + WRITE_PERI_REG(0x60000914, 0x73); + // update_key_led(); + return 0; +} + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE tmr_map[] = +{ + { LSTRKEY( "delay" ), LFUNCVAL( tmr_delay ) }, + { LSTRKEY( "now" ), LFUNCVAL( tmr_now ) }, + { LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) }, + { LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) }, + { LSTRKEY( "wdclr" ), LFUNCVAL( tmr_wdclr ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_tmr( lua_State *L ) +{ + int i = 0; + for(i=0;i 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_TMR, tmr_map ); + // Add constants + + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} diff --git a/app/modules/uart.c b/app/modules/uart.c new file mode 100644 index 00000000..98d3c5dd --- /dev/null +++ b/app/modules/uart.c @@ -0,0 +1,148 @@ +// Module for interfacing with serial + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" + +#include "c_types.h" +#include "c_string.h" + +static lua_State *gL = NULL; +static int uart_receive_rf = LUA_NOREF; +static bool run_input = true; +bool ICACHE_FLASH_ATTR uart_on_data_cb(const char *buf, size_t len){ + if(!buf || len==0) + return false; + if(uart_receive_rf == LUA_NOREF) + return false; + if(!gL) + return false; + lua_rawgeti(gL, LUA_REGISTRYINDEX, uart_receive_rf); + lua_pushlstring(gL, buf, len); + lua_call(gL, 1, 0); + return !run_input; +} + +// Lua: uart.on("method", function, [run_input]) +static int ICACHE_FLASH_ATTR uart_on( lua_State* L ) +{ + size_t sl; + int32_t run = 1; + const char *method = luaL_checklstring( L, 1, &sl ); + if (method == NULL) + return luaL_error( L, "wrong arg type" ); + + // luaL_checkanyfunction(L, 2); + if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION){ + if ( lua_isnumber(L, 3) ){ + run = lua_tointeger(L, 3); + } + lua_pushvalue(L, 2); // copy argument (func) to the top of stack + } else { + lua_pushnil(L); + } + if(sl == 4 && c_strcmp(method, "data") == 0){ + run_input = true; + if(uart_receive_rf != LUA_NOREF){ + luaL_unref(L, LUA_REGISTRYINDEX, uart_receive_rf); + uart_receive_rf = LUA_NOREF; + } + if(!lua_isnil(L, -1)){ + uart_receive_rf = luaL_ref(L, LUA_REGISTRYINDEX); + gL = L; + if(run==0) + run_input = false; + } else { + lua_pop(L, 1); + } + }else{ + lua_pop(L, 1); + return luaL_error( L, "method not supported" ); + } + return 0; +} + +bool uart0_echo = true; +// Lua: actualbaud = setup( id, baud, databits, parity, stopbits, echo ) +static int uart_setup( lua_State* L ) +{ + unsigned id, databits, parity, stopbits, echo = 1; + u32 baud, res; + + id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( uart, id ); + baud = luaL_checkinteger( L, 2 ); + databits = luaL_checkinteger( L, 3 ); + parity = luaL_checkinteger( L, 4 ); + stopbits = luaL_checkinteger( L, 5 ); + if(lua_isnumber(L,6)){ + echo = lua_tointeger(L,6); + if(echo!=0) + uart0_echo = true; + else + uart0_echo = false; + } + + res = platform_uart_setup( id, baud, databits, parity, stopbits ); + lua_pushinteger( L, res ); + return 1; +} + +// Lua: write( id, string1, [string2], ..., [stringn] ) +static int uart_write( lua_State* L ) +{ + int id; + const char* buf; + size_t len, i; + int total = lua_gettop( L ), s; + + id = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( uart, id ); + for( s = 2; s <= total; s ++ ) + { + if( lua_type( L, s ) == LUA_TNUMBER ) + { + len = lua_tointeger( L, s ); + if( len > 255 ) + return luaL_error( L, "invalid number" ); + platform_uart_send( id, ( u8 )len ); + } + else + { + luaL_checktype( L, s, LUA_TSTRING ); + buf = lua_tolstring( L, s, &len ); + for( i = 0; i < len; i ++ ) + platform_uart_send( id, buf[ i ] ); + } + } + return 0; +} + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE uart_map[] = +{ + { LSTRKEY( "setup" ), LFUNCVAL( uart_setup ) }, + { LSTRKEY( "write" ), LFUNCVAL( uart_write ) }, + { LSTRKEY( "on" ), LFUNCVAL( uart_on ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_uart( lua_State *L ) +{ +#if LUA_OPTIMIZE_MEMORY > 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_UART, uart_map ); + // Add constants + + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} diff --git a/app/modules/wifi.c b/app/modules/wifi.c new file mode 100644 index 00000000..3748cd3e --- /dev/null +++ b/app/modules/wifi.c @@ -0,0 +1,431 @@ +// Module for interfacing with WIFI + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" + +#include "c_string.h" + +#include "c_types.h" +#include "user_interface.h" +#include "smart.h" + +static int wifi_smart_succeed = LUA_NOREF; + +static void ICACHE_FLASH_ATTR wifi_smart_succeed_cb(void *arg){ + NODE_DBG("wifi_smart_succeed_cb is called.\n"); + if( !arg ) + return; + if(wifi_smart_succeed == LUA_NOREF) + return; + lua_State* L = (lua_State *)arg; + lua_rawgeti(L, LUA_REGISTRYINDEX, wifi_smart_succeed); + lua_call(L, 0, 0); +} + +static int wifi_scan_succeed = LUA_NOREF; +static lua_State* gL = NULL; +/** + * @brief Wifi ap scan over callback to display. + * @param arg: contain the aps information + * @param status: scan over status + * @retval None + */ +static void ICACHE_FLASH_ATTR +wifi_scan_done(void *arg, STATUS status) +{ + uint8 ssid[33]; + char temp[128]; + if(wifi_scan_succeed == LUA_NOREF) + return; + if(arg == NULL) + return; + + lua_rawgeti(gL, LUA_REGISTRYINDEX, wifi_scan_succeed); + + if (status == OK) + { + struct bss_info *bss_link = (struct bss_info *)arg; + bss_link = bss_link->next.stqe_next;//ignore first + lua_newtable( gL ); + + while (bss_link != NULL) + { + c_memset(ssid, 0, 33); + if (c_strlen(bss_link->ssid) <= 32) + { + c_memcpy(ssid, bss_link->ssid, c_strlen(bss_link->ssid)); + } + else + { + c_memcpy(ssid, bss_link->ssid, 32); + } + c_sprintf(temp,"%d,%d,"MACSTR",%d", bss_link->authmode, bss_link->rssi, + MAC2STR(bss_link->bssid),bss_link->channel); + + lua_pushstring(gL, temp); + lua_setfield( gL, -2, ssid ); + + // NODE_DBG(temp); + + bss_link = bss_link->next.stqe_next; + } + } + else + { + lua_pushnil(gL); + } + lua_call(gL, 1, 0); +} + +// Lua: smart(channel, function succeed_cb) +static int ICACHE_FLASH_ATTR wifi_start_smart( lua_State* L ) +{ + unsigned channel; + int stack = 1; + + if ( lua_isnumber(L, stack) ){ + channel = lua_tointeger(L, stack); + stack++; + } else { + channel = 6; + } + + // luaL_checkanyfunction(L, stack); + if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){ + lua_pushvalue(L, stack); // copy argument (func) to the top of stack + if(wifi_smart_succeed != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, wifi_smart_succeed); + wifi_smart_succeed = luaL_ref(L, LUA_REGISTRYINDEX); + } + + if ( channel > 14 || channel < 1 ) + return luaL_error( L, "wrong arg range" ); + + if(wifi_smart_succeed == LUA_NOREF){ + smart_begin(channel, NULL, NULL); + }else{ + smart_begin(channel, (smart_succeed )wifi_smart_succeed_cb, L); + } + return 0; +} + +// Lua: exit_smart(channel) +static int ICACHE_FLASH_ATTR wifi_exit_smart( lua_State* L ) +{ + smart_end(); + if(wifi_smart_succeed != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, wifi_smart_succeed); + wifi_smart_succeed = LUA_NOREF; + return 0; +} + +// Lua: realmode = setmode(mode) +static int ICACHE_FLASH_ATTR wifi_setmode( lua_State* L ) +{ + unsigned mode; + + mode = luaL_checkinteger( L, 1 ); + + if ( mode != STATION_MODE && mode != SOFTAP_MODE && mode != STATIONAP_MODE ) + return luaL_error( L, "wrong arg type" ); + wifi_set_opmode( (uint8_t)mode); + mode = (unsigned)wifi_get_opmode(); + lua_pushinteger( L, mode ); + return 1; +} + +// Lua: realmode = getmode() +static int ICACHE_FLASH_ATTR wifi_getmode( lua_State* L ) +{ + unsigned mode; + mode = (unsigned)wifi_get_opmode(); + lua_pushinteger( L, mode ); + return 1; +} + + +// Lua: mac = wifi.xx.getmac() +static int ICACHE_FLASH_ATTR wifi_getmac( lua_State* L, uint8_t mode ) +{ + char temp[64]; + uint8_t mac[6]; + wifi_get_macaddr(mode, mac); + c_sprintf(temp, "%02X-%02X-%02X-%02X-%02X-%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); + lua_pushstring( L, temp ); + return 1; +} + +// Lua: ip = wifi.xx.getip() +static int ICACHE_FLASH_ATTR wifi_getip( lua_State* L, uint8_t mode ) +{ + struct ip_info pTempIp; + char temp[64]; + wifi_get_ip_info(mode, &pTempIp); + if(pTempIp.ip.addr==0){ + lua_pushnil(L); + } else { + c_sprintf(temp, "%d.%d.%d.%d", IP2STR(&pTempIp.ip) ); + lua_pushstring( L, temp ); + } + return 1; +} + +// Lua: wifi.sta.getmac() +static int ICACHE_FLASH_ATTR wifi_station_getmac( lua_State* L ){ + return wifi_getmac(L, STATION_IF); +} + +// Lua: wifi.sta.getip() +static int ICACHE_FLASH_ATTR wifi_station_getip( lua_State* L ){ + return wifi_getip(L, STATION_IF); +} + +// Lua: wifi.sta.config(ssid, password) +static int ICACHE_FLASH_ATTR wifi_station_config( lua_State* L ) +{ + size_t sl, pl; + struct station_config sta_conf; + int i; + const char *ssid = luaL_checklstring( L, 1, &sl ); + if (sl>32 || ssid == NULL) + return luaL_error( L, "ssid:<32" ); + const char *password = luaL_checklstring( L, 2, &pl ); + if (pl>64 || password == NULL) + return luaL_error( L, "pwd:<64" ); + + c_memset(sta_conf.ssid, 0, 32); + c_memset(sta_conf.password, 0, 64); + c_memset(sta_conf.bssid, 0, 6); + c_memcpy(sta_conf.ssid, ssid, sl); + c_memcpy(sta_conf.password, password, pl); + sta_conf.bssid_set = 0; + + NODE_DBG(sta_conf.ssid); + NODE_DBG(" %d\n", sl); + NODE_DBG(sta_conf.password); + NODE_DBG(" %d\n", pl); + + wifi_station_set_config(&sta_conf); + wifi_station_set_auto_connect(true); + wifi_station_disconnect(); + wifi_station_connect(); + // station_check_connect(0); + return 0; +} + +// Lua: wifi.sta.connect() +static int ICACHE_FLASH_ATTR wifi_station_connect4lua( lua_State* L ) +{ + wifi_station_connect(); + return 0; +} + +// Lua: wifi.sta.disconnect() +static int ICACHE_FLASH_ATTR wifi_station_disconnect4lua( lua_State* L ) +{ + wifi_station_disconnect(); + return 0; +} + +// Lua: wifi.sta.auto(true/false) +static int ICACHE_FLASH_ATTR wifi_station_setauto( lua_State* L ) +{ + unsigned a; + + a = luaL_checkinteger( L, 1 ); + + if ( a != 0 && a != 1 ) + return luaL_error( L, "wrong arg type" ); + wifi_station_set_auto_connect(a); + if(a){ + // station_check_connect(0); + } + return 0; +} + +static int ICACHE_FLASH_ATTR wifi_station_listap( lua_State* L ) +{ + if(wifi_get_opmode() == SOFTAP_MODE) + { + return luaL_error( L, "Can't list ap in SOFTAP mode" ); + } + gL = L; + // luaL_checkanyfunction(L, 1); + if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION){ + lua_pushvalue(L, 1); // copy argument (func) to the top of stack + if(wifi_scan_succeed != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, wifi_scan_succeed); + wifi_scan_succeed = luaL_ref(L, LUA_REGISTRYINDEX); + wifi_station_scan(NULL,wifi_scan_done); + } else { + if(wifi_scan_succeed != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, wifi_scan_succeed); + wifi_scan_succeed = LUA_NOREF; + } +} + +// Lua: wifi.sta.status() +static int ICACHE_FLASH_ATTR wifi_station_status( lua_State* L ) +{ + uint8_t status = wifi_station_get_connect_status(); + lua_pushinteger( L, status ); + return 1; +} + +// Lua: wifi.ap.getmac() +static int ICACHE_FLASH_ATTR wifi_ap_getmac( lua_State* L ){ + return wifi_getmac(L, SOFTAP_IF); +} + +// Lua: wifi.ap.getip() +static int ICACHE_FLASH_ATTR wifi_ap_getip( lua_State* L ){ + return wifi_getip(L, SOFTAP_IF); +} + +// Lua: wifi.ap.config(table) +static int ICACHE_FLASH_ATTR wifi_ap_config( lua_State* L ) +{ + struct softap_config config; + size_t len; + wifi_softap_get_config(&config); + if (!lua_istable(L, 1)) + return luaL_error( L, "wrong arg type" ); + + lua_getfield(L, 1, "ssid"); + if (!lua_isnil(L, -1)){ /* found? */ + if( lua_isstring(L, -1) ) // deal with the ssid string + { + const char *ssid = luaL_checklstring( L, -1, &len ); + if(len>32) + return luaL_error( L, "ssid:<32" ); + c_memset(config.ssid, 0, 32); + c_memcpy(config.ssid, ssid, len); + config.ssid_len = len; + config.ssid_hidden = 0; + NODE_DBG(config.ssid); + NODE_DBG("\n"); + } + else + return luaL_error( L, "wrong arg type" ); + } + else + return luaL_error( L, "wrong arg type" ); + + lua_getfield(L, 1, "pwd"); + if (!lua_isnil(L, -1)){ /* found? */ + if( lua_isstring(L, -1) ) // deal with the password string + { + const char *pwd = luaL_checklstring( L, -1, &len ); + if(len>64) + return luaL_error( L, "pwd:<64" ); + c_memset(config.password, 0, 64); + c_memcpy(config.password, pwd, len); + config.authmode = AUTH_WPA_WPA2_PSK; + NODE_DBG(config.password); + NODE_DBG("\n"); + } + else + return luaL_error( L, "wrong arg type" ); + } + else{ + config.authmode = AUTH_OPEN; + } + + config.max_connection = 4; + + wifi_softap_set_config(&config); + // system_restart(); + return 0; +} + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +static const LUA_REG_TYPE wifi_station_map[] = +{ + { LSTRKEY( "config" ), LFUNCVAL ( wifi_station_config ) }, + { LSTRKEY( "connect" ), LFUNCVAL ( wifi_station_connect4lua ) }, + { LSTRKEY( "disconnect" ), LFUNCVAL ( wifi_station_disconnect4lua ) }, + { LSTRKEY( "autoconnect" ), LFUNCVAL ( wifi_station_setauto ) }, + { LSTRKEY( "getip" ), LFUNCVAL ( wifi_station_getip ) }, + { LSTRKEY( "getmac" ), LFUNCVAL ( wifi_station_getmac ) }, + { LSTRKEY( "getap" ), LFUNCVAL ( wifi_station_listap ) }, + { LSTRKEY( "status" ), LFUNCVAL ( wifi_station_status ) }, + { LNILKEY, LNILVAL } +}; + +static const LUA_REG_TYPE wifi_ap_map[] = +{ + { LSTRKEY( "config" ), LFUNCVAL( wifi_ap_config ) }, + { LSTRKEY( "getip" ), LFUNCVAL ( wifi_ap_getip ) }, + { LSTRKEY( "getmac" ), LFUNCVAL ( wifi_ap_getmac ) }, + { LNILKEY, LNILVAL } +}; + +const LUA_REG_TYPE wifi_map[] = +{ + { LSTRKEY( "setmode" ), LFUNCVAL( wifi_setmode ) }, + { LSTRKEY( "getmode" ), LFUNCVAL( wifi_getmode ) }, + { LSTRKEY( "startsmart" ), LFUNCVAL( wifi_start_smart ) }, + { LSTRKEY( "stopsmart" ), LFUNCVAL( wifi_exit_smart ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + { LSTRKEY( "sta" ), LROVAL( wifi_station_map ) }, + { LSTRKEY( "ap" ), LROVAL( wifi_ap_map ) }, + + // { LSTRKEY( "NULLMODE" ), LNUMVAL( NULL_MODE ) }, + { LSTRKEY( "STATION" ), LNUMVAL( STATION_MODE ) }, + { LSTRKEY( "SOFTAP" ), LNUMVAL( SOFTAP_MODE ) }, + { LSTRKEY( "STATIONAP" ), LNUMVAL( STATIONAP_MODE ) }, + + // { LSTRKEY( "STA_IDLE" ), LNUMVAL( STATION_IDLE ) }, + // { LSTRKEY( "STA_CONNECTING" ), LNUMVAL( STATION_CONNECTING ) }, + // { LSTRKEY( "STA_WRONGPWD" ), LNUMVAL( STATION_WRONG_PASSWORD ) }, + // { LSTRKEY( "STA_APNOTFOUND" ), LNUMVAL( STATION_NO_AP_FOUND ) }, + // { LSTRKEY( "STA_FAIL" ), LNUMVAL( STATION_CONNECT_FAIL ) }, + // { LSTRKEY( "STA_GOTIP" ), LNUMVAL( STATION_GOT_IP ) }, + + { LSTRKEY( "__metatable" ), LROVAL( wifi_map ) }, +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_wifi( lua_State *L ) +{ +#if LUA_OPTIMIZE_MEMORY > 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_WIFI, wifi_map ); + + // Set it as its own metatable + lua_pushvalue( L, -1 ); + lua_setmetatable( L, -2 ); + + // Module constants + // MOD_REG_NUMBER( L, "NULLMODE", NULL_MODE ); + MOD_REG_NUMBER( L, "STATION", STATION_MODE ); + MOD_REG_NUMBER( L, "SOFTAP", SOFTAP_MODE ); + MOD_REG_NUMBER( L, "STATIONAP", STATIONAP_MODE ); + + // MOD_REG_NUMBER( L, "STA_IDLE", STATION_IDLE ); + // MOD_REG_NUMBER( L, "STA_CONNECTING", STATION_CONNECTING ); + // MOD_REG_NUMBER( L, "STA_WRONGPWD", STATION_WRONG_PASSWORD ); + // MOD_REG_NUMBER( L, "STA_APNOTFOUND", STATION_NO_AP_FOUND ); + // MOD_REG_NUMBER( L, "STA_FAIL", STATION_CONNECT_FAIL ); + // MOD_REG_NUMBER( L, "STA_GOTIP", STATION_GOT_IP ); + + // Setup the new tables (station and ap) inside wifi + lua_newtable( L ); + luaL_register( L, NULL, wifi_station_map ); + lua_setfield( L, -2, "sta" ); + + lua_newtable( L ); + luaL_register( L, NULL, wifi_ap_map ); + lua_setfield( L, -2, "ap" ); + + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} diff --git a/app/platform/Makefile b/app/platform/Makefile new file mode 100644 index 00000000..8a6dd55f --- /dev/null +++ b/app/platform/Makefile @@ -0,0 +1,47 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +GEN_LIBS = libplatform.a +endif + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ../wofs +INCLUDES += -I ../spiffs +INCLUDES += -I ../libc +INCLUDES += -I ../lua +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/platform/common.c b/app/platform/common.c new file mode 100644 index 00000000..ae6a4e9f --- /dev/null +++ b/app/platform/common.c @@ -0,0 +1,260 @@ +// Common code for all backends + +#include "platform.h" +#include "common.h" +#include "c_string.h" +#include "c_stdio.h" + +void ICACHE_FLASH_ATTR cmn_platform_init(void) +{ + +} + +// **************************************************************************** +// GPIO functions + +int ICACHE_FLASH_ATTR platform_gpio_exists( unsigned pin ) +{ + return pin < NUM_GPIO; +} + +// **************************************************************************** +// CAN functions + +int ICACHE_FLASH_ATTR platform_can_exists( unsigned id ) +{ + return id < NUM_CAN; +} + +// **************************************************************************** +// SPI functions + + +int ICACHE_FLASH_ATTR platform_spi_exists( unsigned id ) +{ + return id < NUM_SPI; +} + +// **************************************************************************** +// PWM functions + +int ICACHE_FLASH_ATTR platform_pwm_exists( unsigned id ) +{ + return ((id < NUM_PWM) && (id > 0)); +} + +// **************************************************************************** +// ADC functions + +int ICACHE_FLASH_ATTR platform_adc_exists( unsigned id ) +{ + return id < NUM_ADC; +} + +// **************************************************************************** +// UART functions + +int ICACHE_FLASH_ATTR platform_uart_exists( unsigned id ) +{ + return id < NUM_UART; +} + +// **************************************************************************** +// OneWire functions + +int ICACHE_FLASH_ATTR platform_ow_exists( unsigned id ) +{ + return ((id < NUM_OW) && (id > 0)); +} + +// **************************************************************************** +// Timer functions + +int ICACHE_FLASH_ATTR platform_tmr_exists( unsigned id ) +{ + return id < NUM_TMR; +} + +// I2C support +int ICACHE_FLASH_ATTR platform_i2c_exists( unsigned id ) +{ +#ifndef NUM_I2C + return 0; +#else + return id < NUM_I2C; +#endif +} + +// **************************************************************************** +// Internal flash support functions + +// This symbol must be exported by the linker command file and must reflect the +// TOTAL size of flash used by the eLua image (not only the code and constants, +// but also .data and whatever else ends up in the eLua image). FS will start +// at the next usable (aligned to a flash sector boundary) address after +// flash_used_size. + +// extern char flash_used_size[]; +extern char _flash_used_end[]; + +// Helper function: find the flash sector in which an address resides +// Return the sector number, as well as the start and end address of the sector +static uint32_t ICACHE_FLASH_ATTR flashh_find_sector( uint32_t address, uint32_t *pstart, uint32_t *pend ) +{ + address -= INTERNAL_FLASH_START_ADDRESS; +#ifdef INTERNAL_FLASH_SECTOR_SIZE + // All the sectors in the flash have the same size, so just align the address + uint32_t sect_id = address / INTERNAL_FLASH_SECTOR_SIZE; + + if( pstart ) + *pstart = sect_id * INTERNAL_FLASH_SECTOR_SIZE + INTERNAL_FLASH_START_ADDRESS; + if( pend ) + *pend = ( sect_id + 1 ) * INTERNAL_FLASH_SECTOR_SIZE + INTERNAL_FLASH_START_ADDRESS - 1; + return sect_id; +#else // #ifdef INTERNAL_FLASH_SECTOR_SIZE + // The flash has blocks of different size + // Their size is decribed in the INTERNAL_FLASH_SECTOR_ARRAY macro + const uint32_t flash_sect_size[] = INTERNAL_FLASH_SECTOR_ARRAY; + uint32_t total = 0, i = 0; + + while( ( total <= address ) && ( i < sizeof( flash_sect_size ) / sizeof( uint32_t ) ) ) + total += flash_sect_size[ i ++ ]; + if( pstart ) + *pstart = ( total - flash_sect_size[ i - 1 ] ) + INTERNAL_FLASH_START_ADDRESS; + if( pend ) + *pend = total + INTERNAL_FLASH_START_ADDRESS - 1; + return i - 1; +#endif // #ifdef INTERNAL_FLASH_SECTOR_SIZE +} + +uint32_t ICACHE_FLASH_ATTR platform_flash_get_sector_of_address( uint32_t addr ) +{ + return flashh_find_sector( addr, NULL, NULL ); +} + +uint32_t ICACHE_FLASH_ATTR platform_flash_get_num_sectors(void) +{ +#ifdef INTERNAL_FLASH_SECTOR_SIZE + return INTERNAL_FLASH_SIZE / INTERNAL_FLASH_SECTOR_SIZE; +#else // #ifdef INTERNAL_FLASH_SECTOR_SIZE + const uint32_t flash_sect_size[] = INTERNAL_FLASH_SECTOR_ARRAY; + + return sizeof( flash_sect_size ) / sizeof( uint32_t ); +#endif // #ifdef INTERNAL_FLASH_SECTOR_SIZE +} + +uint32_t ICACHE_FLASH_ATTR platform_flash_get_first_free_block_address( uint32_t *psect ) +{ + // Round the total used flash size to the closest flash block address + uint32_t start, end, sect; + NODE_DBG("_flash_used_end:%08x\n", (uint32_t)_flash_used_end); + if(_flash_used_end>0){ // find the used sector + // sect = flashh_find_sector( ( uint32_t )flash_used_size + INTERNAL_FLASH_START_ADDRESS - 1, NULL, &end ); + sect = flashh_find_sector( ( uint32_t )_flash_used_end - 1, NULL, &end ); + if( psect ) + *psect = sect + 1; + return end + 1; + }else{ + sect = flashh_find_sector( INTERNAL_FLASH_START_ADDRESS, &start, NULL ); // find the first free sector + if( psect ) + *psect = sect; + return start; + } +} + +uint32_t ICACHE_FLASH_ATTR platform_flash_write( const void *from, uint32_t toaddr, uint32_t size ) +{ +#ifndef INTERNAL_FLASH_WRITE_UNIT_SIZE + return platform_s_flash_write( from, toaddr, size ); +#else // #ifindef INTERNAL_FLASH_WRITE_UNIT_SIZE + uint32_t temp, rest, ssize = size; + unsigned i; + char tmpdata[ INTERNAL_FLASH_WRITE_UNIT_SIZE ]; + const uint8_t *pfrom = ( const uint8_t* )from; + const uint32_t blksize = INTERNAL_FLASH_WRITE_UNIT_SIZE; + const uint32_t blkmask = INTERNAL_FLASH_WRITE_UNIT_SIZE - 1; + + // Align the start + if( toaddr & blkmask ) + { + rest = toaddr & blkmask; + temp = toaddr & ~blkmask; // this is the actual aligned address + // c_memcpy( tmpdata, ( const void* )temp, blksize ); + platform_s_flash_read( tmpdata, temp, blksize ); + for( i = rest; size && ( i < blksize ); i ++, size --, pfrom ++ ) + tmpdata[ i ] = *pfrom; + platform_s_flash_write( tmpdata, temp, blksize ); + if( size == 0 ) + return ssize; + toaddr = temp + blksize; + } + // The start address is now a multiple of blksize + // Compute how many bytes we can write as multiples of blksize + rest = size & blkmask; + temp = size & ~blkmask; + // Program the blocks now + if( temp ) + { + platform_s_flash_write( pfrom, toaddr, temp ); + toaddr += temp; + pfrom += temp; + } + // And the final part of a block if needed + if( rest ) + { + // c_memcpy( tmpdata, ( const void* )toaddr, blksize ); + platform_s_flash_read( tmpdata, toaddr, blksize ); + for( i = 0; size && ( i < rest ); i ++, size --, pfrom ++ ) + tmpdata[ i ] = *pfrom; + platform_s_flash_write( tmpdata, toaddr, blksize ); + } + return ssize; +#endif // #ifndef INTERNAL_FLASH_WRITE_UNIT_SIZE +} + +uint32_t ICACHE_FLASH_ATTR platform_flash_read( void *to, uint32_t fromaddr, uint32_t size ) +{ +#ifndef INTERNAL_FLASH_READ_UNIT_SIZE + return platform_s_flash_read( to, fromaddr, size ); +#else // #ifindef INTERNAL_FLASH_READ_UNIT_SIZE + uint32_t temp, rest, ssize = size; + unsigned i; + char tmpdata[ INTERNAL_FLASH_READ_UNIT_SIZE ]; + uint8_t *pto = ( uint8_t* )to; + const uint32_t blksize = INTERNAL_FLASH_READ_UNIT_SIZE; + const uint32_t blkmask = INTERNAL_FLASH_READ_UNIT_SIZE - 1; + + // Align the start + if( fromaddr & blkmask ) + { + rest = fromaddr & blkmask; + temp = fromaddr & ~blkmask; // this is the actual aligned address + platform_s_flash_read( tmpdata, temp, blksize ); + for( i = rest; size && ( i < blksize ); i ++, size --, pto ++ ) + *pto = tmpdata[ i ]; + + if( size == 0 ) + return ssize; + fromaddr = temp + blksize; + } + // The start address is now a multiple of blksize + // Compute how many bytes we can read as multiples of blksize + rest = size & blkmask; + temp = size & ~blkmask; + // Program the blocks now + if( temp ) + { + platform_s_flash_read( pto, fromaddr, temp ); + fromaddr += temp; + pto += temp; + } + // And the final part of a block if needed + if( rest ) + { + platform_s_flash_read( tmpdata, fromaddr, blksize ); + for( i = 0; size && ( i < rest ); i ++, size --, pto ++ ) + *pto = tmpdata[ i ]; + } + return ssize; +#endif // #ifndef INTERNAL_FLASH_READ_UNIT_SIZE +} diff --git a/app/platform/common.h b/app/platform/common.h new file mode 100644 index 00000000..7ea6f269 --- /dev/null +++ b/app/platform/common.h @@ -0,0 +1,18 @@ +// Common platform functions + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include "platform.h" + +// Functions exported by the common platform layer +void cmn_platform_init(void); + +// Timer-specific functions + +// System timer generic implemenation + +// Filesystem-related functions + +#endif // #ifndef __COMMON_H__ + diff --git a/app/platform/cpu_esp8266.h b/app/platform/cpu_esp8266.h new file mode 100644 index 00000000..ecccee93 --- /dev/null +++ b/app/platform/cpu_esp8266.h @@ -0,0 +1,54 @@ +#ifndef __CPU_ESP8266_H__ +#define __CPU_ESP8266_H__ + +#include "os_type.h" +#include "spi_flash.h" +#include "pin_map.h" +#include "user_config.h" +// Number of resources (0 if not available/not implemented) +#define NUM_GPIO GPIO_PIN_NUM +#define NUM_SPI 1 +#define NUM_UART 1 +#define NUM_PWM GPIO_PIN_NUM +#define NUM_ADC 1 +#define NUM_CAN 0 +#define NUM_I2C 1 +#define NUM_OW GPIO_PIN_NUM +#define NUM_TMR 7 + +#if defined(FLASH_512K) +#define FLASH_SEC_NUM 0x80 // 4MByte: 0x400, 2MByte: 0x200, 1MByte: 0x100, 512KByte: 0x80 +#elif defined(FLASH_1M) +#define FLASH_SEC_NUM 0x100 +#elif defined(FLASH_2M) +#define FLASH_SEC_NUM 0x200 +#elif defined(FLASH_4M) +#define FLASH_SEC_NUM 0x400 +#else +#define FLASH_SEC_NUM 0x80 +#endif +#define SYS_PARAM_SEC_NUM 4 +#define SYS_PARAM_SEC_START (FLASH_SEC_NUM - SYS_PARAM_SEC_NUM) + +// #define WOFS_SEC_START 0x80 +// #define WOFS_SEC_START 0x60 +// #define WOFS_SEC_END (SYS_PARAM_SEC_START) +// #define WOFS_SEC_NUM (WOFS_SEC_END - WOFS_SEC_START) +// #define WOFS_SEC_NUM 0xc + +#define INTERNAL_FLASH_SECTOR_SIZE SPI_FLASH_SEC_SIZE +// #define INTERNAL_FLASH_SECTOR_ARRAY { 0x4000, 0x4000, 0x4000, 0x4000, 0x10000, 0x20000, 0x20000, 0x20000, 0x20000, 0x20000 } +#define INTERNAL_FLASH_WRITE_UNIT_SIZE 4 +#define INTERNAL_FLASH_READ_UNIT_SIZE 4 + +#define INTERNAL_FLASH_SIZE ( (SYS_PARAM_SEC_START) * INTERNAL_FLASH_SECTOR_SIZE ) +#define INTERNAL_FLASH_START_ADDRESS 0x40200000 + +// SpiFlashOpResult spi_flash_erase_sector(uint16 sec); +// SpiFlashOpResult spi_flash_write(uint32 des_addr, uint32 *src_addr, uint32 size); +// SpiFlashOpResult spi_flash_read(uint32 src_addr, uint32 *des_addr, uint32 size); +#define flash_write spi_flash_write +#define flash_erase spi_flash_erase_sector +#define flash_read spi_flash_read + +#endif // #ifndef __CPU_ESP8266_H__ diff --git a/app/platform/flash_fs.c b/app/platform/flash_fs.c new file mode 100644 index 00000000..b8f6373a --- /dev/null +++ b/app/platform/flash_fs.c @@ -0,0 +1,32 @@ +#include "flash_fs.h" +#include "c_string.h" + +#if defined( BUILD_WOFS ) +#include "romfs.h" +#elif defined( BUILD_SPIFFS ) +#include "spiffs.h" +#endif + +int ICACHE_FLASH_ATTR fs_mode2flag(const char *mode){ + if(c_strlen(mode)==1){ + if(c_strcmp(mode,"w")==0) + return FS_WRONLY|FS_CREAT|FS_TRUNC; + else if(c_strcmp(mode, "r")==0) + return FS_RDONLY; + else if(c_strcmp(mode, "a")==0) + return FS_WRONLY|FS_CREAT|FS_APPEND; + else + return FS_RDONLY; + } else if (c_strlen(mode)==2){ + if(c_strcmp(mode,"r+")==0) + return FS_RDWR; + else if(c_strcmp(mode, "w+")==0) + return FS_RDWR|FS_CREAT|FS_TRUNC; + else if(c_strcmp(mode, "a+")==0) + return FS_RDWR|FS_CREAT|FS_APPEND; + else + return FS_RDONLY; + } else { + return FS_RDONLY; + } +} diff --git a/app/platform/flash_fs.h b/app/platform/flash_fs.h new file mode 100644 index 00000000..3ab0421a --- /dev/null +++ b/app/platform/flash_fs.h @@ -0,0 +1,78 @@ + +#ifndef __FLASH_FS_H__ +#define __FLASH_FS_H__ + +#include "user_config.h" + +#if defined( BUILD_WOFS ) +#include "romfs.h" + +#define FS_OPEN_OK 0 + +#define FS_RDONLY O_RDONLY +#define FS_WRONLY O_WRONLY +#define FS_RDWR O_RDWR +#define FS_APPEND O_APPEND +#define FS_TRUNC O_TRUNC +#define FS_CREAT O_CREAT +#define FS_EXCL O_EXCL + +#define FS_SEEK_SET SEEK_SET +#define FS_SEEK_CUR SEEK_CUR +#define FS_SEEK_END SEEK_END + +#define fs_open wofs_open +#define fs_close wofs_close +#define fs_write wofs_write +#define fs_read wofs_read +#define fs_seek wofs_lseek +#define fs_eof wofs_eof +#define fs_getc wofs_getc +#define fs_ungetc wofs_ungetc + +#define fs_format wofs_format +#define fs_next wofs_next + +#define FS_NAME_MAX_LENGTH MAX_FNAME_LENGTH + +#elif defined( BUILD_SPIFFS ) + +#include "spiffs.h" + +#define FS_OPEN_OK 1 + +#define FS_RDONLY SPIFFS_RDONLY +#define FS_WRONLY SPIFFS_WRONLY +#define FS_RDWR SPIFFS_RDWR +#define FS_APPEND SPIFFS_APPEND +#define FS_TRUNC SPIFFS_TRUNC +#define FS_CREAT SPIFFS_CREAT +#define FS_EXCL SPIFFS_EXCL + +#define FS_SEEK_SET SPIFFS_SEEK_SET +#define FS_SEEK_CUR SPIFFS_SEEK_CUR +#define FS_SEEK_END SPIFFS_SEEK_END + +#define fs_open myspiffs_open +#define fs_close myspiffs_close +#define fs_write myspiffs_write +#define fs_read myspiffs_read +#define fs_seek myspiffs_lseek +#define fs_eof myspiffs_eof +#define fs_getc myspiffs_getc +#define fs_ungetc myspiffs_ungetc +#define fs_flush myspiffs_flush +#define fs_error myspiffs_error +#define fs_clearerr myspiffs_clearerr +#define fs_tell myspiffs_tell + +#define fs_format myspiffs_format +#define fs_check myspiffs_check + +#define FS_NAME_MAX_LENGTH SPIFFS_OBJ_NAME_LEN + +#endif + +int fs_mode2flag(const char *mode); + +#endif // #ifndef __FLASH_FS_H__ diff --git a/app/platform/pin_map.c b/app/platform/pin_map.c new file mode 100644 index 00000000..a3f64859 --- /dev/null +++ b/app/platform/pin_map.c @@ -0,0 +1,45 @@ +#include "pin_map.h" +#include "eagle_soc.h" +#if 0 +uint32_t pin_mux[GPIO_PIN_NUM] = {PERIPHS_IO_MUX_MTDI_U, PERIPHS_IO_MUX_MTCK_U, PERIPHS_IO_MUX_MTMS_U, PERIPHS_IO_MUX_MTDO_U, + PERIPHS_IO_MUX_U0RXD_U, PERIPHS_IO_MUX_U0TXD_U, PERIPHS_IO_MUX_SD_DATA2_U, PERIPHS_IO_MUX_SD_DATA3_U, + PERIPHS_IO_MUX_GPIO0_U, PERIPHS_IO_MUX_GPIO2_U, PERIPHS_IO_MUX_GPIO4_U, PERIPHS_IO_MUX_GPIO5_U}; + +uint8_t pin_num[GPIO_PIN_NUM] = {12, 13, 14, 15, + 3, 1, 9, 10, + 0, 2, 4, 5}; + +uint8_t pin_func[GPIO_PIN_NUM] = {FUNC_GPIO12, FUNC_GPIO13, FUNC_GPIO14, FUNC_GPIO15, + FUNC_GPIO3, FUNC_GPIO1, FUNC_GPIO9, FUNC_GPIO10, + FUNC_GPIO0, FUNC_GPIO2, FUNC_GPIO4, FUNC_GPIO5}; + +#ifdef GPIO_INTERRUPT_ENABLE +GPIO_INT_TYPE pin_int_type[GPIO_PIN_NUM] = { + GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, + GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, + GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE}; +#endif +#else +uint32_t pin_mux[GPIO_PIN_NUM] = {PAD_XPD_DCDC_CONF, PERIPHS_IO_MUX_GPIO4_U, PERIPHS_IO_MUX_GPIO5_U, PERIPHS_IO_MUX_GPIO0_U, + PERIPHS_IO_MUX_GPIO2_U, PERIPHS_IO_MUX_MTMS_U, PERIPHS_IO_MUX_MTDI_U, PERIPHS_IO_MUX_MTCK_U, + PERIPHS_IO_MUX_MTDO_U, PERIPHS_IO_MUX_U0RXD_U, PERIPHS_IO_MUX_U0TXD_U, PERIPHS_IO_MUX_SD_DATA2_U, + PERIPHS_IO_MUX_SD_DATA3_U }; + +uint8_t pin_num[GPIO_PIN_NUM] = {16, 4, 5, 0, + 2, 14, 12, 13, + 15, 3, 1, 9, + 10}; + +uint8_t pin_func[GPIO_PIN_NUM] = {0, FUNC_GPIO4, FUNC_GPIO5, FUNC_GPIO0, + FUNC_GPIO2, FUNC_GPIO14, FUNC_GPIO12, FUNC_GPIO13, + FUNC_GPIO15, FUNC_GPIO3, FUNC_GPIO1, FUNC_GPIO9, + FUNC_GPIO10}; + +#ifdef GPIO_INTERRUPT_ENABLE +GPIO_INT_TYPE pin_int_type[GPIO_PIN_NUM] = { + GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, + GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, + GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, + GPIO_PIN_INTR_DISABLE}; +#endif +#endif diff --git a/app/platform/pin_map.h b/app/platform/pin_map.h new file mode 100644 index 00000000..5b4a68dd --- /dev/null +++ b/app/platform/pin_map.h @@ -0,0 +1,17 @@ + +#ifndef __PIN_MAP_H__ +#define __PIN_MAP_H__ + +#include "c_types.h" +#include "user_config.h" +#include "gpio.h" + +#define GPIO_PIN_NUM 13 + +extern uint8_t pin_num[GPIO_PIN_NUM]; +extern uint8_t pin_func[GPIO_PIN_NUM]; +extern uint32_t pin_mux[GPIO_PIN_NUM]; +#ifdef GPIO_INTERRUPT_ENABLE +extern GPIO_INT_TYPE pin_int_type[GPIO_PIN_NUM]; +#endif +#endif // #ifndef __PIN_MAP_H__ diff --git a/app/platform/platform.c b/app/platform/platform.c new file mode 100644 index 00000000..b69599c2 --- /dev/null +++ b/app/platform/platform.c @@ -0,0 +1,483 @@ +// Platform-dependent functions + +#include "platform.h" +#include "c_stdio.h" +#include "c_string.h" +#include "c_stdlib.h" +#include "gpio.h" +#include "user_interface.h" +#include "driver/uart.h" +// Platform specific includes + +static void pwms_init(); + +int ICACHE_FLASH_ATTR platform_init() +{ + // Setup PWMs + pwms_init(); + + cmn_platform_init(); + // All done + return PLATFORM_OK; +} + +// **************************************************************************** +// KEY_LED functions +uint8_t ICACHE_FLASH_ATTR platform_key_led( uint8_t level){ + uint8_t temp; + gpio16_output_set(1); // set to high first, for reading key low level + gpio16_input_conf(); + temp = gpio16_input_get(); + gpio16_output_conf(); + gpio16_output_set(level); + return temp; +} + +// **************************************************************************** +// GPIO functions +#ifdef GPIO_INTERRUPT_ENABLE +extern void lua_gpio_unref(unsigned pin); +#endif +int ICACHE_FLASH_ATTR platform_gpio_mode( unsigned pin, unsigned mode, unsigned pull ) +{ + // NODE_DBG("Function platform_gpio_mode() is called. pin_mux:%d, func:%d\n",pin_mux[pin],pin_func[pin]); + if (pin >= NUM_GPIO) + return -1; + if(pin == 0){ + if(mode==PLATFORM_GPIO_INPUT) + gpio16_input_conf(); + else + gpio16_output_conf(); + return 1; + } + + platform_pwm_close(pin); // closed from pwm module, if it is used in pwm + + switch(pull){ + case PLATFORM_GPIO_PULLUP: + PIN_PULLDWN_DIS(pin_mux[pin]); + PIN_PULLUP_EN(pin_mux[pin]); + break; + case PLATFORM_GPIO_PULLDOWN: + PIN_PULLUP_DIS(pin_mux[pin]); + PIN_PULLDWN_EN(pin_mux[pin]); + break; + case PLATFORM_GPIO_FLOAT: + PIN_PULLUP_DIS(pin_mux[pin]); + PIN_PULLDWN_DIS(pin_mux[pin]); + break; + default: + PIN_PULLUP_DIS(pin_mux[pin]); + PIN_PULLDWN_DIS(pin_mux[pin]); + break; + } + + switch(mode){ + case PLATFORM_GPIO_INPUT: +#ifdef GPIO_INTERRUPT_ENABLE + lua_gpio_unref(pin); // unref the lua ref call back. +#endif + GPIO_DIS_OUTPUT(pin_num[pin]); + case PLATFORM_GPIO_OUTPUT: + ETS_GPIO_INTR_DISABLE(); +#ifdef GPIO_INTERRUPT_ENABLE + pin_int_type[pin] = GPIO_PIN_INTR_DISABLE; +#endif + PIN_FUNC_SELECT(pin_mux[pin], pin_func[pin]); + //disable interrupt + gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[pin]), GPIO_PIN_INTR_DISABLE); + //clear interrupt status + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(pin_num[pin])); + GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[pin])), GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[pin]))) & (~ GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE))); //disable open drain; + ETS_GPIO_INTR_ENABLE(); + break; +#ifdef GPIO_INTERRUPT_ENABLE + case PLATFORM_GPIO_INT: + ETS_GPIO_INTR_DISABLE(); + PIN_FUNC_SELECT(pin_mux[pin], pin_func[pin]); + GPIO_DIS_OUTPUT(pin_num[pin]); + gpio_register_set(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[pin])), GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE) + | GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE) + | GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE)); + ETS_GPIO_INTR_ENABLE(); + break; +#endif + default: + break; + } + return 1; +} + +int ICACHE_FLASH_ATTR platform_gpio_write( unsigned pin, unsigned level ) +{ + // NODE_DBG("Function platform_gpio_write() is called. pin:%d, level:%d\n",GPIO_ID_PIN(pin_num[pin]),level); + if (pin >= NUM_GPIO) + return -1; + if(pin == 0){ + gpio16_output_conf(); + gpio16_output_set(level); + return 1; + } + + GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), level); +} + +int ICACHE_FLASH_ATTR platform_gpio_read( unsigned pin ) +{ + // NODE_DBG("Function platform_gpio_read() is called. pin:%d\n",GPIO_ID_PIN(pin_num[pin])); + if (pin >= NUM_GPIO) + return -1; + + if(pin == 0){ + gpio16_input_conf(); + return 0x1 & gpio16_input_get(); + } + + GPIO_DIS_OUTPUT(pin_num[pin]); + return 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(pin_num[pin])); +} + +#ifdef GPIO_INTERRUPT_ENABLE +static void ICACHE_FLASH_ATTR platform_gpio_intr_dispatcher( platform_gpio_intr_handler_fn_t cb){ + uint8 i, level; + uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); + for (i = 0; i < GPIO_PIN_NUM; i++) { + if (pin_int_type[i] && (gpio_status & BIT(pin_num[i])) ) { + //disable interrupt + gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[i]), GPIO_PIN_INTR_DISABLE); + //clear interrupt status + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(pin_num[i])); + level = 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(pin_num[i])); + if(cb){ + cb(i, level); + } + gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[i]), pin_int_type[i]); + } + } +} + +void ICACHE_FLASH_ATTR platform_gpio_init( platform_gpio_intr_handler_fn_t cb ) +{ + ETS_GPIO_INTR_ATTACH(platform_gpio_intr_dispatcher, cb); +} + +int ICACHE_FLASH_ATTR platform_gpio_intr_init( unsigned pin, GPIO_INT_TYPE type ) +{ + if (pin >= NUM_GPIO) + return -1; + ETS_GPIO_INTR_DISABLE(); + //clear interrupt status + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(pin_num[pin])); + pin_int_type[pin] = type; + //enable interrupt + gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[pin]), type); + ETS_GPIO_INTR_ENABLE(); +} +#endif + +// **************************************************************************** +// UART +// TODO: Support timeouts. + +// UartDev is defined and initialized in rom code. +extern UartDevice UartDev; +uint32_t ICACHE_FLASH_ATTR platform_uart_setup( unsigned id, uint32_t baud, int databits, int parity, int stopbits ) +{ + switch( baud ) + { + case BIT_RATE_9600: + case BIT_RATE_19200: + case BIT_RATE_38400: + case BIT_RATE_57600: + case BIT_RATE_74880: + case BIT_RATE_115200: + case BIT_RATE_230400: + case BIT_RATE_460800: + case BIT_RATE_921600: + UartDev.baut_rate = baud; + break; + default: + UartDev.baut_rate = BIT_RATE_9600; + break; + } + + switch( databits ) + { + case 5: + UartDev.data_bits = FIVE_BITS; + break; + case 6: + UartDev.data_bits = SIX_BITS; + break; + case 7: + UartDev.data_bits = SEVEN_BITS; + break; + case 8: + UartDev.data_bits = EIGHT_BITS; + break; + default: + UartDev.data_bits = EIGHT_BITS; + break; + } + + switch (stopbits) + { + case PLATFORM_UART_STOPBITS_1: + UartDev.stop_bits = ONE_STOP_BIT; + break; + case PLATFORM_UART_STOPBITS_2: + UartDev.stop_bits = TWO_STOP_BIT; + break; + default: + UartDev.stop_bits = ONE_STOP_BIT; + break; + } + + switch (parity) + { + case PLATFORM_UART_PARITY_EVEN: + UartDev.parity = EVEN_BITS; + break; + case PLATFORM_UART_PARITY_ODD: + UartDev.parity = ODD_BITS; + break; + default: + UartDev.parity = NONE_BITS; + break; + } + + uart_setup(id); + + return baud; +} + +// Send: version with and without mux +void platform_uart_send( unsigned id, u8 data ) +{ + uart_tx_one_char(id, data); +} + +// **************************************************************************** +// PWMs + +static uint16_t pwms_duty[NUM_PWM] = {0}; + +static void ICACHE_FLASH_ATTR pwms_init() +{ + int i; + for(i=0;i= NUM_PWM) + return 0; + if(!pwm_exist(pin)) + return 0; + + return (uint32_t)pwm_get_freq(pin); +} + +// Set the PWM clock +uint32_t ICACHE_FLASH_ATTR platform_pwm_set_clock( unsigned pin, uint32_t clock ) +{ + // NODE_DBG("Function platform_pwm_set_clock() is called.\n"); + if( pin >= NUM_PWM) + return 0; + if(!pwm_exist(pin)) + return 0; + + pwm_set_freq((uint16_t)clock, pin); + pwm_start(); + return (uint32_t)pwm_get_freq( pin ); +} + +uint32_t ICACHE_FLASH_ATTR platform_pwm_get_duty( unsigned pin ) +{ + // NODE_DBG("Function platform_pwm_get_duty() is called.\n"); + if( pin < NUM_PWM){ + if(!pwm_exist(pin)) + return 0; + // return NORMAL_DUTY(pwm_get_duty(pin)); + return pwms_duty[pin]; + } + return 0; +} + +// Set the PWM duty +uint32_t ICACHE_FLASH_ATTR platform_pwm_set_duty( unsigned pin, uint32_t duty ) +{ + // NODE_DBG("Function platform_pwm_set_duty() is called.\n"); + if ( pin < NUM_PWM) + { + if(!pwm_exist(pin)) + return 0; + pwm_set_duty(DUTY(duty), pin); + } else { + return 0; + } + pwm_start(); + pwms_duty[pin] = NORMAL_DUTY(pwm_get_duty(pin)); + return pwms_duty[pin]; +} + +uint32_t ICACHE_FLASH_ATTR platform_pwm_setup( unsigned pin, uint32_t frequency, unsigned duty ) +{ + uint32_t clock; + if ( pin < NUM_PWM) + { + platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT); // disable gpio interrupt first + if(!pwm_add(pin)) + return 0; + // pwm_set_duty(DUTY(duty), pin); + pwm_set_duty(0, pin); + pwms_duty[pin] = duty; + pwm_set_freq((uint16_t)frequency, pin); + } else { + return 0; + } + clock = platform_pwm_get_clock( pin ); + pwm_start(); + return clock; +} + +void ICACHE_FLASH_ATTR platform_pwm_close( unsigned pin ) +{ + // NODE_DBG("Function platform_pwm_stop() is called.\n"); + if ( pin < NUM_PWM) + { + pwm_delete(pin); + pwm_start(); + } +} + +void ICACHE_FLASH_ATTR platform_pwm_start( unsigned pin ) +{ + // NODE_DBG("Function platform_pwm_start() is called.\n"); + if ( pin < NUM_PWM) + { + if(!pwm_exist(pin)) + return; + pwm_set_duty(DUTY(pwms_duty[pin]), pin); + pwm_start(); + } +} + +void ICACHE_FLASH_ATTR platform_pwm_stop( unsigned pin ) +{ + // NODE_DBG("Function platform_pwm_stop() is called.\n"); + if ( pin < NUM_PWM) + { + if(!pwm_exist(pin)) + return; + pwm_set_duty(0, pin); + pwm_start(); + } +} + +// ***************************************************************************** +// I2C platform interface + +uint32_t ICACHE_FLASH_ATTR platform_i2c_setup( unsigned id, uint8_t sda, uint8_t scl, uint32_t speed ){ + if (sda >= NUM_GPIO || scl >= NUM_GPIO) + return 0; + + // platform_pwm_close(sda); + // platform_pwm_close(scl); + + // disable gpio interrupt first + platform_gpio_mode(sda, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP); // inside this func call platform_pwm_close + platform_gpio_mode(scl, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP); // disable gpio interrupt first + + i2c_master_gpio_init(sda, scl); + return PLATFORM_I2C_SPEED_SLOW; +} + +void ICACHE_FLASH_ATTR platform_i2c_send_start( unsigned id ){ + i2c_master_start(); +} + +void ICACHE_FLASH_ATTR platform_i2c_send_stop( unsigned id ){ + i2c_master_stop(); +} + +int ICACHE_FLASH_ATTR platform_i2c_send_address( unsigned id, uint16_t address, int direction ){ + // Convert enum codes to R/w bit value. + // If TX == 0 and RX == 1, this test will be removed by the compiler + if ( ! ( PLATFORM_I2C_DIRECTION_TRANSMITTER == 0 && + PLATFORM_I2C_DIRECTION_RECEIVER == 1 ) ) { + direction = ( direction == PLATFORM_I2C_DIRECTION_TRANSMITTER ) ? 0 : 1; + } + + i2c_master_writeByte( (uint8_t) ((address << 1) | direction )); + // Low-level returns nack (0=acked); we return ack (1=acked). + return ! i2c_master_getAck(); +} + +int ICACHE_FLASH_ATTR platform_i2c_send_byte( unsigned id, uint8_t data ){ + i2c_master_writeByte(data); + // Low-level returns nack (0=acked); we return ack (1=acked). + return ! i2c_master_getAck(); +} + +int ICACHE_FLASH_ATTR platform_i2c_recv_byte( unsigned id, int ack ){ + uint8_t r = i2c_master_readByte(); + i2c_master_setAck( !ack ); + return r; +} + +// **************************************************************************** +// Flash access functions + +uint32_t ICACHE_FLASH_ATTR platform_s_flash_write( const void *from, uint32_t toaddr, uint32_t size ) +{ + toaddr -= INTERNAL_FLASH_START_ADDRESS; + SpiFlashOpResult r; + const uint32_t blkmask = INTERNAL_FLASH_WRITE_UNIT_SIZE - 1; + uint32_t *apbuf = NULL; + if( ((uint32_t)from) & blkmask ){ + apbuf = (uint32_t *)c_malloc(size); + if(!apbuf) + return 0; + c_memcpy(apbuf, from, size); + } + WRITE_PERI_REG(0x60000914, 0x73); + r = flash_write(toaddr, apbuf?(uint32 *)apbuf:(uint32 *)from, size); + if(apbuf) + c_free(apbuf); + if(SPI_FLASH_RESULT_OK == r) + return size; + else{ + NODE_ERR( "ERROR in flash_write: r=%d at %08X\n", ( int )r, ( unsigned )toaddr+INTERNAL_FLASH_START_ADDRESS ); + return 0; + } +} + +uint32_t ICACHE_FLASH_ATTR platform_s_flash_read( void *to, uint32_t fromaddr, uint32_t size ) +{ + fromaddr -= INTERNAL_FLASH_START_ADDRESS; + SpiFlashOpResult r; + WRITE_PERI_REG(0x60000914, 0x73); + r = flash_read(fromaddr, (uint32 *)to, size); + if(SPI_FLASH_RESULT_OK == r) + return size; + else{ + NODE_ERR( "ERROR in flash_read: r=%d at %08X\n", ( int )r, ( unsigned )fromaddr+INTERNAL_FLASH_START_ADDRESS ); + return 0; + } +} + +int ICACHE_FLASH_ATTR platform_flash_erase_sector( uint32_t sector_id ) +{ + WRITE_PERI_REG(0x60000914, 0x73); + return flash_erase( sector_id ) == SPI_FLASH_RESULT_OK ? PLATFORM_OK : PLATFORM_ERR; +} diff --git a/app/platform/platform.h b/app/platform/platform.h new file mode 100644 index 00000000..286dab2d --- /dev/null +++ b/app/platform/platform.h @@ -0,0 +1,232 @@ +// Platform-specific functions + +#ifndef __PLATFORM_H__ +#define __PLATFORM_H__ + +#include "cpu_esp8266.h" + +#include "c_types.h" +#include "driver/pwm.h" +// Error / status codes +enum +{ + PLATFORM_ERR, + PLATFORM_OK, + PLATFORM_UNDERFLOW = -1 +}; + +// Platform initialization +int platform_init(void); +void platform_int_init(void); + +// **************************************************************************** +// KEY_LED functions +uint8_t platform_key_led( uint8_t level); + +// ***************************************************************************** +// GPIO subsection +#define PLATFORM_GPIO_FLOAT 0 +#define PLATFORM_GPIO_PULLUP 1 +#define PLATFORM_GPIO_PULLDOWN 2 + +#define PLATFORM_GPIO_INT 2 +#define PLATFORM_GPIO_OUTPUT 1 +#define PLATFORM_GPIO_INPUT 0 + +#define PLATFORM_GPIO_HIGH 1 +#define PLATFORM_GPIO_LOW 0 + +/* GPIO interrupt handler */ +typedef void (* platform_gpio_intr_handler_fn_t)( unsigned pin, unsigned level ); + +int platform_gpio_mode( unsigned pin, unsigned mode, unsigned pull ); +int platform_gpio_write( unsigned pin, unsigned level ); +int platform_gpio_read( unsigned pin ); +void platform_gpio_init( platform_gpio_intr_handler_fn_t cb ); +int platform_gpio_intr_init( unsigned pin, GPIO_INT_TYPE type ); +// ***************************************************************************** +// Timer subsection + +// Timer data type +typedef uint32_t timer_data_type; + +// ***************************************************************************** +// CAN subsection + +// Maximum length for any CAN message +#define PLATFORM_CAN_MAXLEN 8 + +// eLua CAN ID types +enum +{ + ELUA_CAN_ID_STD = 0, + ELUA_CAN_ID_EXT +}; + +int platform_can_exists( unsigned id ); +uint32_t platform_can_setup( unsigned id, uint32_t clock ); +int platform_can_send( unsigned id, uint32_t canid, uint8_t idtype, uint8_t len, const uint8_t *data ); +int platform_can_recv( unsigned id, uint32_t *canid, uint8_t *idtype, uint8_t *len, uint8_t *data ); + +// ***************************************************************************** +// SPI subsection + +// There are 4 "virtual" SPI ports (SPI0...SPI3). +#define PLATFORM_SPI_TOTAL 4 +// TODO: PLATFORM_SPI_TOTAL is not used - figure out purpose, or remove? + +// SPI mode +#define PLATFORM_SPI_MASTER 1 +#define PLATFORM_SPI_SLAVE 0 +// SS values +#define PLATFORM_SPI_SELECT_ON 1 +#define PLATFORM_SPI_SELECT_OFF 0 +// SPI enable/disable +#define PLATFORM_SPI_ENABLE 1 +#define PLATFORM_SPI_DISABLE 0 + +// Data types +typedef uint32_t spi_data_type; + +// The platform SPI functions +int platform_spi_exists( unsigned id ); +uint32_t platform_spi_setup( unsigned id, int mode, uint32_t clock, unsigned cpol, unsigned cpha, unsigned databits ); +spi_data_type platform_spi_send_recv( unsigned id, spi_data_type data ); +void platform_spi_select( unsigned id, int is_select ); + +// ***************************************************************************** +// UART subsection + +// There are 3 "virtual" UART ports (UART0...UART2). +#define PLATFORM_UART_TOTAL 3 +// TODO: PLATFORM_UART_TOTAL is not used - figure out purpose, or remove? +// Note: Some CPUs (e.g. LM4F/TM4C) have more than 3 hardware UARTs + +// Parity +enum +{ + PLATFORM_UART_PARITY_EVEN, + PLATFORM_UART_PARITY_ODD, + PLATFORM_UART_PARITY_NONE, + PLATFORM_UART_PARITY_MARK, + PLATFORM_UART_PARITY_SPACE +}; + +// Stop bits +enum +{ + PLATFORM_UART_STOPBITS_1, + PLATFORM_UART_STOPBITS_1_5, + PLATFORM_UART_STOPBITS_2 +}; + +// Flow control types (this is a bit mask, one can specify PLATFORM_UART_FLOW_RTS | PLATFORM_UART_FLOW_CTS ) +#define PLATFORM_UART_FLOW_NONE 0 +#define PLATFORM_UART_FLOW_RTS 1 +#define PLATFORM_UART_FLOW_CTS 2 + +// The platform UART functions +int platform_uart_exists( unsigned id ); +uint32_t platform_uart_setup( unsigned id, uint32_t baud, int databits, int parity, int stopbits ); +int platform_uart_set_buffer( unsigned id, unsigned size ); +void platform_uart_send( unsigned id, uint8_t data ); +void platform_s_uart_send( unsigned id, uint8_t data ); +int platform_uart_recv( unsigned id, unsigned timer_id, timer_data_type timeout ); +int platform_s_uart_recv( unsigned id, timer_data_type timeout ); +int platform_uart_set_flow_control( unsigned id, int type ); +int platform_s_uart_set_flow_control( unsigned id, int type ); + +// ***************************************************************************** +// PWM subsection + +// There are 16 "virtual" PWM channels (PWM0...PWM15) +#define PLATFORM_PWM_TOTAL 16 +// TODO: PLATFORM_PWM_TOTAL is not used - figure out purpose, or remove? + +#define NORMAL_PWM_DEPTH PWM_DEPTH +#define NORMAL_DUTY(d) (((unsigned)(d)*NORMAL_PWM_DEPTH) / PWM_DEPTH) +#define DUTY(d) ((uint16_t)( ((unsigned)(d)*PWM_DEPTH) / NORMAL_PWM_DEPTH) ) + +// The platform PWM functions +int platform_pwm_exists( unsigned id ); +uint32_t platform_pwm_setup( unsigned id, uint32_t frequency, unsigned duty ); +void platform_pwm_close( unsigned id ); +void platform_pwm_start( unsigned id ); +void platform_pwm_stop( unsigned id ); +uint32_t platform_pwm_set_clock( unsigned id, uint32_t data ); +uint32_t platform_pwm_get_clock( unsigned id ); +uint32_t platform_pwm_set_duty( unsigned id, uint32_t data ); +uint32_t platform_pwm_get_duty( unsigned id ); + + +// ***************************************************************************** +// The platform ADC functions + +// Functions requiring platform-specific implementation +int platform_adc_update_sequence(void); +int platform_adc_start_sequence(void); +void platform_adc_stop( unsigned id ); +uint32_t platform_adc_set_clock( unsigned id, uint32_t frequency); +int platform_adc_check_timer_id( unsigned id, unsigned timer_id ); + +// ADC Common Functions +int platform_adc_exists( unsigned id ); +uint32_t platform_adc_get_maxval( unsigned id ); +uint32_t platform_adc_set_smoothing( unsigned id, uint32_t length ); +void platform_adc_set_blocking( unsigned id, uint32_t mode ); +void platform_adc_set_freerunning( unsigned id, uint32_t mode ); +uint32_t platform_adc_is_done( unsigned id ); +void platform_adc_set_timer( unsigned id, uint32_t timer ); + +// ***************************************************************************** +// I2C platform interface + +// I2C speed +enum +{ + PLATFORM_I2C_SPEED_SLOW = 100000, + PLATFORM_I2C_SPEED_FAST = 400000 +}; + +// I2C direction +enum +{ + PLATFORM_I2C_DIRECTION_TRANSMITTER, + PLATFORM_I2C_DIRECTION_RECEIVER +}; + +int platform_i2c_exists( unsigned id ); +uint32_t platform_i2c_setup( unsigned id, uint8_t sda, uint8_t scl, uint32_t speed ); +void platform_i2c_send_start( unsigned id ); +void platform_i2c_send_stop( unsigned id ); +int platform_i2c_send_address( unsigned id, uint16_t address, int direction ); +int platform_i2c_send_byte( unsigned id, uint8_t data ); +int platform_i2c_recv_byte( unsigned id, int ack ); + +// ***************************************************************************** +// Ethernet specific functions + +void platform_eth_send_packet( const void* src, uint32_t size ); +uint32_t platform_eth_get_packet_nb( void* buf, uint32_t maxlen ); +void platform_eth_force_interrupt(void); +uint32_t platform_eth_get_elapsed_time(void); + +// ***************************************************************************** +// Internal flash erase/write functions + +uint32_t platform_flash_get_first_free_block_address( uint32_t *psect ); +uint32_t platform_flash_get_sector_of_address( uint32_t addr ); +uint32_t platform_flash_write( const void *from, uint32_t toaddr, uint32_t size ); +uint32_t platform_flash_read( void *to, uint32_t fromaddr, uint32_t size ); +uint32_t platform_s_flash_write( const void *from, uint32_t toaddr, uint32_t size ); +uint32_t platform_s_flash_read( void *to, uint32_t fromaddr, uint32_t size ); +uint32_t platform_flash_get_num_sectors(void); +int platform_flash_erase_sector( uint32_t sector_id ); + +// ***************************************************************************** +// Allocator support + +void* platform_get_first_free_ram( unsigned id ); +void* platform_get_last_free_ram( unsigned id ); + +#endif diff --git a/app/smart/Makefile b/app/smart/Makefile new file mode 100644 index 00000000..79d73da1 --- /dev/null +++ b/app/smart/Makefile @@ -0,0 +1,44 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +GEN_LIBS = smart.a +endif + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ../libc +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/smart/smart.c b/app/smart/smart.c new file mode 100644 index 00000000..71808de5 --- /dev/null +++ b/app/smart/smart.c @@ -0,0 +1,713 @@ +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_string.h" +#include "user_interface.h" +#include "smart.h" + +#define ADDR_MAP_NUM 10 + +static os_timer_t smart_timer; + +static smart_addr_map *am[ADDR_MAP_NUM]; + +static smart_addr_map *matched = NULL; + +static struct station_config *sta_conf; + +static int cur_channel = 1; + +static uint8_t mode = STATION_MODE; + +static uint8_t alldone = 0; + +// 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000(LSB) +// when the bit is set, means the ssid byte is got +static uint8_t *got_ssid = NULL; +static uint8_t *got_password = NULL; + +static uint8_t *ssid_nibble = NULL; +static uint8_t *password_nibble = NULL; + +static smart_succeed succeed = NULL; +static void *smart_succeed_arg = NULL; + +void smart_end(); +int ICACHE_FLASH_ATTR smart_check(uint8_t *nibble, uint16_t len, uint8_t *dst, uint8_t *got){ + if(len == 0) + return 0; + uint16_t dst_len = len/NIBBLE_PER_BYTE; + uint16_t byte_num = 0, bit_num = 0; + int i = 0, res = 1; // assume ok. + c_memset(dst,0,dst_len); + + if(NIBBLE_PER_BYTE==1){ + for(i=0;inibble[i] || nibble[i]>=0x7F){ // not printable + NODE_DBG("Smart: got np byte %d:%02x\n", i, nibble[i]); + nibble[i] = 0; + got[byte_num] &= ~(0x1 << bit_num); // clear the bit + res = 0; // not ok + } else { + dst[i] = nibble[i]; + } + } + return res; + } + + // NIBBLE_PER_BYTE == 2 + if((len%NIBBLE_PER_BYTE) != 0){ + // this should not happen + NODE_DBG("Smart: smart_check got odd len\n"); + return 0; + } + + if(len == 2){ + // only one byte + if(nibble[0]<=0xF && ((nibble[0]^0x1)&0xF == (nibble[1]>>4)) ){ + dst[0] = ((nibble[0]&0xF)<<4) + (nibble[1]&0xF); + res = 1; + }else{ + nibble[0] = 0; + nibble[1] = 0; + got[0] &= ~(0x3 << 0); // clear the 0 bit + res = 0; // not ok + } + return res; + } + + res = 1; // assume ok. + for(i=len-2;i>0;i--){ + bool forward = ( ((nibble[i]&0xF)^((i+1)&0xF)) == (nibble[i+1]>>4) ); + bool back = ( ((nibble[i-1]&0xF)^(i&0xF)) == (nibble[i]>>4) ); + if(!forward || !back){ + // wrong forward, or wrong back, replace i-1, i and i+1, until get right back, forward + NODE_DBG("check: wf %d:%02x %02x %02x\n",i,nibble[i-1],nibble[i], nibble[i+1]); + byte_num = (i-1) / 8; + bit_num = (i-1) % 8; + nibble[i-1] = 0; + got[byte_num] &= ~(0x1 << bit_num); // clear the bit + + byte_num = (i) / 8; + bit_num = (i) % 8; + nibble[i] = 0; + got[byte_num] &= ~(0x1 << bit_num); // clear the bit + + byte_num = (i+1) / 8; + bit_num = (i+1) % 8; + nibble[i+1] = 0; + got[byte_num] &= ~(0x1 << bit_num); // clear the bit + res = 0; + return res; // once there is error, + } + + if((i%NIBBLE_PER_BYTE) == 0) { // i == even + dst[i/NIBBLE_PER_BYTE] = ((nibble[i]&0xF)<<4) + (nibble[i+1]&0xF); + } + } + + if(i==0){ + dst[0] = ((nibble[0]&0xF)<<4) + (nibble[1]&0xF); + } + + for(i=0;i%02x\n", i, nibble[i*NIBBLE_PER_BYTE], nibble[i*NIBBLE_PER_BYTE+1], dst[i]); + byte_num = (i*NIBBLE_PER_BYTE) / 8; + bit_num = (i*NIBBLE_PER_BYTE) % 8; + if(0x20>dst[i] || dst[i]>=0x7F){ // not printable + NODE_DBG("Smart: got np byte %d:%02x\n", i, dst[i]); + dst[i] = 0; // reset byte + nibble[i*NIBBLE_PER_BYTE] = 0; // reset hi-nibble + nibble[i*NIBBLE_PER_BYTE+1] = 0; // reset lo-nibble + got[byte_num] &= ~(0x3 << bit_num); // clear the bit + res = 0; // not ok + } + } + return res; +} + +void ICACHE_FLASH_ATTR detect(uint8 *buf, uint16 len){ + uint16_t seq; + int16_t seq_delta = 0; + uint16_t byte_num = 0, bit_num = 0; + int16_t c = 0; + if( ( (buf[0]) & TYPE_SUBTYPE_MASK) != TYPE_SUBTYPE_QOS_DATA){ + return; + } + if( (buf[1] & DS_RETRY_MASK) != NO_RETRY ) + return; + if( buf[SEQ_ADDR] & 0xF != 0 ) // Fragment Number should = 0 + return; + // calculate current seq number + seq = buf[SEQ_ADDR+1]; + seq = seq<<4; + seq += buf[SEQ_ADDR]>>4; + + if(!matched){ // cur_base_seq is ref to flag[0] when finding the patern + int i; + for (i = 0; i < ADDR_MAP_NUM; i++) // for each source-dest adress pair in the map + { + if ( am[i]->flag_match_num == 0 ){ // not in the map yet + if ( len - am[i]->base_len == am[i]->flag[0]) // store new source-dest adress pair to the map until flag[0] is got + { + // BSSID, SA, DA, store the SA, DA + c_memcpy(am[i]->addr, &buf[ADDR_MATCH_START], ADDR_MATCH_LENGTH); + am[i]->flag_match_num++; // =1 + am[i]->cur_base_seq = seq; // assume the first seq is found + am[i]->base_seq_valid = 1; + // NODE_DBG("Smart: new addr pair found\n"); + } + break; // break any way for the next packet to come + } + else if(0 == c_memcmp(am[i]->addr, &buf[ADDR_MATCH_START], ADDR_MATCH_LENGTH)){ // source-dest adress pair match + if(am[i]->base_seq_valid == 0){ + if ( len - am[i]->base_len == am[i]->flag[0]) { // found the new flag[0] + // here flag_match_num is already = 1 + am[i]->cur_base_seq = seq; + am[i]->base_seq_valid = 1; // the seq number is valid now + // NODE_DBG("Smart: new base_seq found\n"); + } + break; // break any way for the next packet to come + } + + // base seq number is valid, cal the delta + if(seq >= am[i]->cur_base_seq){ + seq_delta = seq - am[i]->cur_base_seq; + } else { + seq_delta = SEQ_MAX - am[i]->cur_base_seq + seq; + } + + if(seq_delta < 0){ // this should never happen + am[i]->base_seq_valid = 0; // the seq number is not valid + break; + } + + if(seq_delta == 0){ // base_seq is not valid any more + if ( len - am[i]->base_len != am[i]->flag[0]) { // lost the flag[0] + am[i]->base_seq_valid = 0; // the seq number is not valid + } + break; // break any way for the next packet to come + } + + // delta is out of range, need to find the next flag[0] to start again + if (seq_delta>=FLAG_NUM){ + am[i]->flag_match_num = 1; // reset to 1 + if ( len - am[i]->base_len == am[i]->flag[0]) { // found the new flag[0] + // here flag_match_num is already = 1 + am[i]->cur_base_seq = seq; + am[i]->base_seq_valid = 1; // the seq number is valid now + } else { + am[i]->base_seq_valid = 0; + } + break; // done for this packet + } + + // NODE_DBG("Smart: match_num:%d seq_delta:%d len:%d\n",am[i]->flag_match_num,seq_delta,len-am[i]->base_len); + // seq_delta now from 1 to FLAG_NUM-1 + // flag[] == 0 ,means skip this flag. + if ( (am[i]->flag_match_num==seq_delta) && \ + ( (am[i]->flag[am[i]->flag_match_num]==len-am[i]->base_len) || (am[i]->flag[am[i]->flag_match_num]==0) ) ){ + am[i]->flag_match_num++; + if(am[i]->flag_match_num == FLAG_MATCH_NUM){ //every thing is match. + NODE_ERR("Smart: got matched sender\n"); + matched = am[i]; // got the matched source-dest adress pair who is sending the udp data + matched->base_seq_valid = 0; // set to 0, and start to reference to the SSID_FLAG from now on + os_timer_disarm(&smart_timer); // note: may start a longer timeout + } + break; + } + + // non match, reset, need to find next flag[0] to start again + am[i]->flag_match_num = 1; + am[i]->base_seq_valid = 0; + break; + } // non-match source-dest adress pair, continue to next pair in the map. + } // for loop + // break out, or loop done. + goto end; + } else { // cur_base_seq is ref to SSID_FLAG when patern is alread found + if(0 != c_memcmp(matched->addr, &buf[ADDR_MATCH_START], ADDR_MATCH_LENGTH)){ // source-dest adress pair not match, ignore it + return; + } + if (matched->base_seq_valid == 0){ // SSID_FLAG seq invalid, need to find the next valid seq number + // base_seq not valid, find it + if (len - matched->base_len == SSID_FLAG){ + matched->cur_base_seq = seq; + matched->base_seq_valid = 1; + } + goto end; + } + + if(seq >= matched->cur_base_seq){ + seq_delta = seq - matched->cur_base_seq; + } else { + seq_delta = SEQ_MAX - matched->cur_base_seq + seq; + } + + if(seq_delta < 0){ // this should never happen + matched->base_seq_valid = 0; // the seq number is not valid + goto end; + } + + if(seq_delta == 0){ // base_seq is not valid any more + if ( len - matched->base_len != SSID_FLAG) { // lost the SSID_FLAG + matched->base_seq_valid = 0; // the seq number is not valid + } + goto end; // exit for the next packet to come + } + + if ( seq_delta > (SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->ssid_len) +\ + 1 + (SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->pwd_len) ){ + // delta out of the range + if (len - matched->base_len == SSID_FLAG){ + matched->cur_base_seq = seq; + matched->base_seq_valid = 1; + } else { + matched->base_seq_valid = 0; + } + goto end; + } + + // delta in the range + if (seq_delta==1){ + int16_t ssid_len = len - matched->base_len - L_FLAG; + if ( matched->ssid_len == 0 ){ // update the ssid_len + if ( (ssid_len <=32) && (ssid_len >0) ){ + matched->ssid_len = ssid_len; + NODE_DBG("Smart: found the ssid_len %d\n", matched->ssid_len); + } + goto end; + } + if (ssid_len != matched->ssid_len){ // ssid_len not match + matched->base_seq_valid = 0; + // note: not match, save the new one or old one? for now save the new one. + matched->ssid_len = ssid_len; + NODE_DBG("Smart: ssid_len not match\n"); + } + goto end; // to the next packet + } + + if( (SEP_NUM==2)&&(seq_delta==2 || seq_delta==3) ) { + if (len - matched->base_len != matched->flag[seq_delta-2+SEP_1_INDEX]){ // SEP not match + matched->base_seq_valid = 0; + NODE_DBG("Smart: SEP-L not match\n"); + } + goto end; // to the next packet + } + + if( seq_delta==(SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->ssid_len) + 1) { + if (len - matched->base_len != PWD_FLAG){ // PWD_FLAG not match + matched->base_seq_valid = 0; + NODE_DBG("Smart: PWD_FLAG not match\n"); + } + goto end; // to the next packet + } + + if (seq_delta==(SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->ssid_len) + 1 + 1){ + int16_t pwd_len = len - matched->base_len - L_FLAG; + if ( matched->pwd_len == 0){ + if ( (pwd_len <=64) && (pwd_len>0)){ + matched->pwd_len = pwd_len; + NODE_DBG("Smart: found the pwd_len %d\n", matched->pwd_len); + } + goto end; // to the next packet + } + if (pwd_len != matched->pwd_len){ // pwd_len not match + matched->base_seq_valid = 0; + // note: not match, save the new one or old one? for now save the new one. + matched->pwd_len = pwd_len; // reset pwd_len to 0 + NODE_DBG("Smart: pwd_len not match\n"); + } + goto end; + } + + if (seq_delta <= (SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->ssid_len) ){ // in the ssid zone + uint16_t it = (seq_delta-1-SEP_NUM-1) / (SEP_NUM + 1); // the number of ssid nibble: 0~31 or 0~63 + uint16_t m = (seq_delta-1-SEP_NUM-1) % (SEP_NUM + 1); // 0~2 + switch(m){ + case 0: // the ssid hi/lo-nibble itself + c = (int16_t)(len - matched->base_len - C_FLAG); + if (c>255 || c<0){ + matched->base_seq_valid = 0; + NODE_DBG("Smart: wrong ssid nibble\n"); + goto end; + } + byte_num = it / 8; // 0~7 + bit_num = it % 8; // 0~7 + if( (got_ssid[byte_num] & (0x1 << bit_num)) == 0){ + got_ssid[byte_num] |= 0x1 << bit_num; // set the bit + ssid_nibble[it] = c; + } + break; + case 1: // seperator 1 + case 2: // seperator 2 + if(len - matched->base_len != matched->flag[m-1+SEP_1_INDEX]){ + NODE_DBG("Smart: SEP-S not match\n"); + matched->base_seq_valid = 0; + goto end; + } + break; + default: + break; + } + } else { // in the pwd zone + uint16_t it = (seq_delta -1 -(SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->ssid_len) - 2 - SEP_NUM) / (SEP_NUM + 1); // the number of pwd byte + uint16_t m = (seq_delta -1 -(SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->ssid_len) - 2 - SEP_NUM) % (SEP_NUM + 1); + switch(m){ + case 0: // the pwd hi/lo-nibble itself + c = (int16_t)(len - matched->base_len - C_FLAG); + if (c>255 || c<0){ + matched->base_seq_valid = 0; + NODE_DBG("Smart: wrong password nibble\n"); + goto end; + } + byte_num = it / 8; // 0~15 / 7 + bit_num = it % 8; // 0~7 + if( (got_password[byte_num] & (0x1 << bit_num)) == 0){ + got_password[byte_num] |= 0x1 << bit_num; // set the bit + password_nibble[it] = c; + } + break; + case 1: // seperator 1 + case 2: // seperator 2 + if(len - matched->base_len != matched->flag[m-1+SEP_1_INDEX]){ + NODE_DBG("Smart: SEP-P not match\n"); + matched->base_seq_valid = 0; + goto end; + } + break; + default: + break; + } + } + // check if all done + // NODE_DBG("Smart: ssid got %02x %02x\n", got_ssid[0], got_ssid[1]); + // NODE_DBG("Smart: password got %02x %02x %02x\n", got_password[0], got_password[1], got_password[2]); + int i,j; + for(i=0;issid_len;i++){ + byte_num = (i) / 8; + bit_num = (i) % 8; + if( (got_ssid[byte_num] & (0x1 << bit_num) ) != (0x1 << bit_num) ){ // check the bit == 1 + break; + } + } + for(j=0;jpwd_len;j++){ + byte_num = (j) / 8; + bit_num = (j) % 8; + if( (got_password[byte_num] & (0x1 << bit_num) ) != (0x1 << bit_num) ){ // check the 2 bit == 11 + break; + } + } + if(matched->ssid_len > 0 && matched->pwd_len > 0 && i==NIBBLE_PER_BYTE*matched->ssid_len && j==NIBBLE_PER_BYTE*matched->pwd_len){ // get everything, check it. + if( smart_check(ssid_nibble, NIBBLE_PER_BYTE*matched->ssid_len, sta_conf->ssid, got_ssid) && \ + smart_check(password_nibble, NIBBLE_PER_BYTE*matched->pwd_len, sta_conf->password, got_password) ){ + // all done + alldone = 1; + NODE_ERR(sta_conf->ssid); + NODE_ERR(" %d\n", matched->ssid_len); + NODE_ERR(sta_conf->password); + NODE_ERR(" %d\n", matched->pwd_len); + smart_end(); + // if(succeed){ + // succeed(smart_succeed_arg); + // succeed = NULL; // reset to NULL when succeed + // smart_succeed_arg = NULL; + // } + return; + } + } + } + +end: +#if 0 + NODE_DBG("%d:\t0x%x 0x%x\t", len-BASE_LENGTH, buf[0],buf[1]); + NODE_DBG(MACSTR, MAC2STR(&(buf[BSSID_ADDR]))); + NODE_DBG("\t"); + NODE_DBG(MACSTR, MAC2STR(&(buf[SOURCE_ADDR]))); + NODE_DBG("\t"); + NODE_DBG(MACSTR, MAC2STR(&(buf[DEST_ADDR]))); + uint16_t tseq = buf[SEQ_ADDR+1]; + tseq = tseq<<4; + tseq += buf[SEQ_ADDR]>>4; + NODE_DBG("\t0x%04x\n", tseq); +#endif + return; +} + +void ICACHE_FLASH_ATTR reset_map(smart_addr_map **am, size_t num){ + int i; + for (i = 0; i < num; ++i) + { + am[i]->flag_match_num = 0; + am[i]->addr_len = ADDR_MATCH_LENGTH; + am[i]->base_len = BASE_LENGTH; + am[i]->cur_base_seq = -1; + am[i]->base_seq_valid = 0; + am[i]->ssid_len = 0; + am[i]->pwd_len = 0; + c_memset(am[i]->addr, 0, ADDR_MATCH_LENGTH); + if(SEP_1_INDEX==0){ + am[i]->flag[0] = SEP_1; + am[i]->flag[1] = SEP_2; + am[i]->flag[2] = SSID_FLAG; + } + if(SEP_1_INDEX==2){ + am[i]->flag[0] = SSID_FLAG; + am[i]->flag[1] = 0; // skip this flag + am[i]->flag[2] = SEP_1; + am[i]->flag[3] = SEP_2; + } + } +} + +void ICACHE_FLASH_ATTR smart_enable(void){ + wifi_promiscuous_enable(1); +} + +void ICACHE_FLASH_ATTR smart_disable(void){ + wifi_promiscuous_enable(0); +} + +void ICACHE_FLASH_ATTR smart_end(){ + int i; + os_timer_disarm(&smart_timer); + smart_disable(); + wifi_set_channel(cur_channel); + + if(NULL_MODE != mode){ + wifi_set_opmode(mode); + } else { + wifi_set_opmode(STATION_MODE); + } + + mode = wifi_get_opmode(); + + if(sta_conf && alldone){ + if( (STATION_MODE == mode) || (mode == STATIONAP_MODE) ){ + wifi_station_set_config(sta_conf); + wifi_station_set_auto_connect(true); + wifi_station_disconnect(); + wifi_station_connect(); + + os_timer_disarm(&smart_timer); + os_timer_setfn(&smart_timer, (os_timer_func_t *)station_check_connect, 1); + os_timer_arm(&smart_timer, STATION_CHECK_TIME, 0); // no repeat + } + } + + for (i = 0; i < ADDR_MAP_NUM; ++i) + { + if(am[i]){ + c_free(am[i]); + am[i] = NULL; + } + matched = NULL; + } + + if(sta_conf){ + c_free(sta_conf); + sta_conf = NULL; + } + + if(got_password){ + c_free(got_password); + got_password = NULL; + } + + if(got_ssid){ + c_free(got_ssid); + got_ssid = NULL; + } + + if(password_nibble){ + c_free(password_nibble); + password_nibble = NULL; + } + + if(ssid_nibble){ + c_free(ssid_nibble); + ssid_nibble = NULL; + } + // system_restart(); // restart to enable the mode +} + +void ICACHE_FLASH_ATTR smart_next_channel(){ + smart_disable(); + switch(cur_channel){ + case 1: + cur_channel = MAX_CHANNEL; + break; + case 2: + case 3: + case 4: + cur_channel++; + break; + case 5: + cur_channel = 7; + break; + case 6: + cur_channel = 1; + break; + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + cur_channel++; + break; + case 13: + cur_channel = 6; + break; + case MAX_CHANNEL: + cur_channel = 2; + break; + default: + cur_channel = 6; + break; + } + + NODE_ERR("switch to channel %d\n", cur_channel); + wifi_set_channel(cur_channel); + reset_map(am, ADDR_MAP_NUM); + c_memset(sta_conf->ssid, 0, sizeof(sta_conf->ssid)); + c_memset(sta_conf->password, 0, sizeof(sta_conf->password)); + + c_memset(got_ssid, 0, SSID_BIT_MAX); + c_memset(got_password, 0, PWD_BIT_MAX); + + c_memset(ssid_nibble, 0, SSID_NIBBLE_MAX); + c_memset(password_nibble, 0, PWD_NIBBLE_MAX); + + os_timer_disarm(&smart_timer); + os_timer_arm(&smart_timer, TIME_OUT_PER_CHANNEL, 0); // no repeat + + smart_enable(); +} + +void ICACHE_FLASH_ATTR smart_begin(int chnl, smart_succeed s, void *arg){ + int i; + alldone = 0; + for (i = 0; i < ADDR_MAP_NUM; ++i) + { + if(!am[i]){ + am[i] = (smart_addr_map*)c_zalloc(sizeof(smart_addr_map)); + if(!am[i]){ + NODE_DBG("smart_begin map no memory\n"); + smart_end(); + return; + } + } + } + if(!sta_conf){ + sta_conf = (struct station_config *)c_zalloc(sizeof(struct station_config)); + if(!sta_conf){ + NODE_DBG("smart_begin sta_conf no memory\n"); + smart_end(); + return; + } + } + + if(!ssid_nibble){ + ssid_nibble = (uint8_t *)c_zalloc(SSID_NIBBLE_MAX); + if(!ssid_nibble){ + NODE_DBG("smart_begin sta_conf no memory\n"); + smart_end(); + return; + } + } + + if(!password_nibble){ + password_nibble = (uint8_t *)c_zalloc(PWD_NIBBLE_MAX); + if(!password_nibble){ + NODE_DBG("smart_begin sta_conf no memory\n"); + smart_end(); + return; + } + } + + if(!got_ssid){ + got_ssid = (uint8_t *)c_zalloc(SSID_BIT_MAX); + if(!got_ssid){ + NODE_DBG("smart_begin sta_conf no memory\n"); + smart_end(); + return; + } + } + + if(!got_password){ + got_password = (uint8_t *)c_zalloc(PWD_BIT_MAX); + if(!got_password){ + NODE_DBG("smart_begin sta_conf no memory\n"); + smart_end(); + return; + } + } + reset_map(am, ADDR_MAP_NUM); + // c_memset(sta_conf->ssid, 0, sizeof(sta_conf->ssid)); + // c_memset(sta_conf->password, 0, sizeof(sta_conf->password)); + + // c_memset(got_ssid, 0, SSID_BIT_MAX); + // c_memset(got_password, 0, PWD_BIT_MAX); + + // c_memset(ssid_nibble, 0, SSID_NIBBLE_MAX); + // c_memset(password_nibble, 0, PWD_NIBBLE_MAX); + mode = wifi_get_opmode(); + if( (STATION_MODE == mode) || (mode == STATIONAP_MODE) ){ + wifi_station_set_auto_connect(false); + wifi_station_disconnect(); + } + cur_channel = chnl; + NODE_ERR("set channel to %d\n", cur_channel); + wifi_set_channel(cur_channel); + wifi_set_promiscuous_rx_cb(detect); + os_timer_disarm(&smart_timer); + os_timer_setfn(&smart_timer, (os_timer_func_t *)smart_next_channel, NULL); + os_timer_arm(&smart_timer, TIME_OUT_PER_CHANNEL, 0); // no repeat + + if(s){ + succeed = s; // init the succeed call back + smart_succeed_arg = arg; + } + + smart_enable(); +} + +void ICACHE_FLASH_ATTR station_check_connect(bool smart){ + mode = wifi_get_opmode(); + if( (STATION_MODE != mode) && (mode != STATIONAP_MODE) ){ + return; + } + uint8_t status = wifi_station_get_connect_status(); + switch(status){ + case STATION_GOT_IP: + NODE_DBG("station_check_connect is called with status: GOT_IP\n"); + if(succeed){ + succeed(smart_succeed_arg); + succeed = NULL; // reset to NULL when succeed + smart_succeed_arg = NULL; + } + return; + case STATION_CONNECTING: + NODE_DBG("station_check_connect is called with status: CONNECTING\n"); + break; + case STATION_IDLE: + wifi_station_set_auto_connect(true); + case STATION_CONNECT_FAIL: + case STATION_NO_AP_FOUND: + wifi_station_disconnect(); + wifi_station_connect(); + NODE_DBG("station_check_connect is called with smart: %d\n", smart); + break; + case STATION_WRONG_PASSWORD: + if(smart) + smart_begin(cur_channel, succeed, smart_succeed_arg); + return; + default: + break; + } + os_timer_disarm(&smart_timer); + os_timer_setfn(&smart_timer, (os_timer_func_t *)station_check_connect, smart); + os_timer_arm(&smart_timer, STATION_CHECK_TIME, 0); // no repeat +} diff --git a/app/smart/smart.h b/app/smart/smart.h new file mode 100644 index 00000000..a702830a --- /dev/null +++ b/app/smart/smart.h @@ -0,0 +1,87 @@ +#ifndef _SMART_H +#define _SMART_H 1 + +#ifdef __cplusplus +extern "C" { +#endif +#define MAX_CHANNEL 14 + +#define TYPE_SUBTYPE_QOS_DATA 0x88 +#define TYPE_SUBTYPE_MASK 0xFC + +#define NO_RETRY 0x41 +#define DS_RETRY_MASK 0x4B + +#define BSSID_ADDR 4 +#define SOURCE_ADDR 10 +#define DEST_ADDR 16 +#define ADDR_LENGTH 6 +#define ADDR_MATCH_START (SOURCE_ADDR) +#define ADDR_MATCH_LENGTH (ADDR_LENGTH*2) + +#define SEQ_ADDR 22 +#define SEQ_LEN 2 +#define SEQ_MAX 0x1000 + +#define BASE_LENGTH 82 + + +#define SSID_FLAG 1399 +#define PWD_FLAG 1459 +#define L_FLAG 28 +#define C_FLAG 593 +#define SEP_1 3 +#define SEP_2 23 +#define SEP_3 24 +#define SEP_4 25 +#define SEP_5 26 +#define SEP_NUM 2 + +#define NIBBLE_PER_BYTE 2 + +#define SEP_1_INDEX 2 + +#if(SEP_1_INDEX==0) +#define FLAG_NUM (SEP_NUM+1) +#elif(SEP_1_INDEX==2) +#define FLAG_NUM (SEP_NUM+2) +#endif +#define FLAG_MATCH_NUM (FLAG_NUM-1) // only need to match 2 or 3 byte to increase speed. + +// #define TI_SMARTCONFIG 1 + +#define SSID_NIBBLE_MAX (32*NIBBLE_PER_BYTE) +#define PWD_NIBBLE_MAX (64*NIBBLE_PER_BYTE) +#define SSID_BIT_MAX (SSID_NIBBLE_MAX/8) +#define PWD_BIT_MAX (PWD_NIBBLE_MAX/8) + +#define TIME_OUT_PER_CHANNEL (30*1000) + +#define STATION_CHECK_TIME (2*1000) + +struct _my_addr_map { + uint8 addr[ADDR_LENGTH*3]; + uint8_t addr_len; + uint16_t base_len; + int16_t flag[FLAG_NUM]; + // flag[0]: SEP_1, flag[1]: SEP_2, flag[1]: SSID_FLAG. SEP followed by SSID_FLAG formed flag[] + // if flag[i]==0, means skip this flag match, eg. SSID_FLAG, 0, SEP_1, SEP_2 + uint8_t flag_match_num; + int16_t cur_base_seq; + int8_t base_seq_valid; + int8_t ssid_len; + int8_t pwd_len; +}; + +typedef struct _my_addr_map smart_addr_map; + +typedef void (* smart_succeed)(void *arg); + +void smart_begin(int chnl, smart_succeed s, void *arg); +void station_check_connect(bool smart); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/spiffs/Makefile b/app/spiffs/Makefile new file mode 100644 index 00000000..1c44e4ac --- /dev/null +++ b/app/spiffs/Makefile @@ -0,0 +1,44 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +GEN_LIBS = spiffs.a +endif + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ../libc +INCLUDES += -I ../platform +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile diff --git a/app/spiffs/docs/IMPLEMENTING b/app/spiffs/docs/IMPLEMENTING new file mode 100644 index 00000000..e69de29b diff --git a/app/spiffs/docs/INTEGRATION b/app/spiffs/docs/INTEGRATION new file mode 100644 index 00000000..703197e4 --- /dev/null +++ b/app/spiffs/docs/INTEGRATION @@ -0,0 +1,283 @@ +* QUICK AND DIRTY INTEGRATION EXAMPLE + +So, assume you're running a Cortex-M3 board with a 2 MB SPI flash on it. The +SPI flash has 64kB blocks. Your project is built using gnumake, and now you +want to try things out. + +First, you simply copy the files in src/ to your own source folder. Exclude +all files in test folder. Then you point out these files in your make script +for compilation. + +Also copy the spiffs_config.h over from the src/default/ folder. + +Try building. This fails, nagging about inclusions and u32_t and whatnot. Open +the spiffs_config.h and delete the bad inclusions. Also, add following +typedefs: + + typedef signed int s32_t; + typedef unsigned int u32_t; + typedef signed short s16_t; + typedef unsigned short u16_t; + typedef signed char s8_t; + typedef unsigned char u8_t; + +Now it should build. Over to the mounting business. Assume you already +implemented the read, write and erase functions to your SPI flash: + + void my_spi_read(int addr, int size, char *buf) + void my_spi_write(int addr, int size, char *buf) + void my_spi_erase(int addr, int size) + +In your main.c or similar, include the spiffs.h and do that spiffs struct: + + #include + + static spiffs fs; + +Also, toss up some of the needed buffers: + + #define LOG_PAGE_SIZE 256 + + static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2]; + static u8_t spiffs_fds[32*4]; + static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*4]; + +Now, write the my_spiffs_mount function: + + void my_spiffs_mount() { + spiffs_config cfg; + cfg.phys_size = 2*1024*1024; // use all spi flash + cfg.phys_addr = 0; // start spiffs at start of spi flash + cfg.phys_erase_block = 65536; // according to datasheet + cfg.log_block_size = 65536; // let us not complicate things + cfg.log_block_size = LOG_PAGE_SIZE; // as we said + + cfg.hal_read_f = my_spi_read; + cfg.hal_write_f = my_spi_write; + cfg.hal_erase_f = my_spi_erase; + + int res = SPIFFS_mount(&fs, + &cfg, + spiffs_work_buf, + spiffs_fds, + sizeof(spiffs_fds), + spiffs_cache, + sizeof(spiffs_cache), + 0); + printf("mount res: %i\n", res); + } + +Now, build warns about the my_spi_read, write and erase functions. Wrong +signatures, so go wrap them: + + static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) { + my_spi_read(addr, size, dst); + return SPIFFS_OK; + } + + static s32_t my_spiffs_write(u32_t addr, u32_t size, u8_t *src) { + my_spi_write(addr, size, dst); + return SPIFFS_OK; + } + + static s32_t my_spiffs_erase(u32_t addr, u32_t size) { + my_spi_erase(addr, size); + return SPIFFS_OK; + } + +Redirect the config in my_spiffs_mount to the wrappers instead: + + cfg.hal_read_f = my_spiffs_read; + cfg.hal_write_f = my_spiffs_write; + cfg.hal_erase_f = my_spiffs_erase; + +Ok, now you should be able to build and run. However, you get this output: + + mount res: -1 + +but you wanted + + mount res: 0 + +This is probably due to you having experimented with your SPI flash, so it +contains rubbish from spiffs's point of view. Do a mass erase and run again. + +If all is ok now, you're good to go. Try creating a file and read it back: + + static void test_spiffs() { + char buf[12]; + + // Surely, I've mounted spiffs before entering here + + spiffs_file fd = SPIFFS_open(&fs, "my_file", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0); + if (SPIFFS_write(&fs, fd, (u8_t *)"Hello world", 12) < 0) printf("errno %i\n", SPIFFS_errno(&fs)); + SPIFFS_close(&fs, fd); + + fd = SPIFFS_open(&fs, "my_file", SPIFFS_RDWR, 0); + if (SPIFFS_read(&fs, fd, (u8_t *)buf, 12) < 0) printf("errno %i\n", SPIFFS_errno(&fs)); + SPIFFS_close(&fs, fd); + + printf("--> %s <--\n", buf); + } + +Compile, run, cross fingers hard, and you'll get the output: + + --> Hello world <-- + +Got errors? Check spiffs.h for error definitions to get a clue what went voodoo. + + +* INTEGRATING SPIFFS + +In order to integrate spiffs to your embedded target, you will basically need: + - A SPI flash device which your processor can communicate with + - An implementation for reading, writing and erasing the flash + - Memory (flash or ram) for the code + - Memory (ram) for the stack + +Other stuff may be needed, threaded systems might need mutexes and so on. + +** Logical structure + +First and foremost, one must decide how to divide up the SPI flash for spiffs. +Having the datasheet for the actual SPI flash in hand will help. Spiffs can be +defined to use all or only parts of the SPI flash. + +If following seems arcane, read the "HOW TO CONFIG" chapter first. + + - Decide the logical size of blocks. This must be a multiple of the biggest + physical SPI flash block size. To go safe, use the physical block size - + which in many cases is 65536 bytes. + - Decide the logical size of pages. This must be a 2nd logarithm part of the + logical block size. To go safe, use 256 bytes to start with. + - Decide how much of the SPI flash memory to be used for spiffs. This must be + on logical block boundary. If unsafe, use 1 megabyte to start with. + - Decide where on the SPI flash memory the spiffs area should start. This must + be on physical block/sector boundary. If unsafe, use address 0. + +** SPI flash API + +The target must provide three functions to spiffs: + + - s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst) + - s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src) + - s32_t (*spiffs_erase)(u32_t addr, u32_t size) + +These functions define the only communication between the SPI flash and the +spiffs stack. + +On success these must return 0 (or SPIFFS_OK). Anything else will be considered +an error. + +The size for read and write requests will never exceed the logical page size, +but it may be less. + +The address and size on erase requests will always be on physical block size +boundaries. + +** Mount specification + +In spiffs.h, there is a SPIFFS_mount function defined, used to mount spiffs on +the SPI flash. + +s32_t SPIFFS_mount( + spiffs *fs, + spiffs_config *config, + u8_t *work, + u8_t *fd_space, + u32_t fd_space_size, + void *cache, + u32_t cache_size, + spiffs_check_callback check_cb_f) + + - fs Points to a spiffs struct. This may be totally uninitialized. + - config Points to a spiffs_config struct. This struct must be + initialized when mounting. See below. + - work A ram memory buffer being double the size of the logical page + size. This buffer is used excessively by the spiffs stack. If + logical page size is 256, this buffer must be 512 bytes. + - fd_space A ram memory buffer used for file descriptors. + - fd_space_size The size of the file descriptor buffer. A file descriptor + normally is around 32 bytes depending on the build config - + the bigger the buffer, the more file descriptors are + available. + - cache A ram memory buffer used for cache. Ignored if cache is + disabled in build config. + - cache_size The size of the cache buffer. Ignored if cache is disabled in + build config. One cache page will be slightly larger than the + logical page size. The more ram, the more cache pages, the + quicker the system. + - check_cb_f Callback function for monitoring spiffs consistency checks and + mending operations. May be null. + +The config struct must be initialized prior to mounting. One must always +define the SPI flash access functions: + + spiffs_config.hal_read_f - pointing to the function reading the SPI flash + + spiffs_config.hal_write_f - pointing to the function writing the SPI flash + + spiffs_config.hal_erase_f - pointing to the function erasing the SPI flash + +Depending on the build config - if SPIFFS_SINGLETON is set to zero - following +parameters must be defined: + + spiffs_config.phys_size - the physical number of bytes accounted for + spiffs on the SPI flash + + spiffs_config.phys_addr - the physical starting address on the SPI flash + + spiffs_config.phys_erase_block - the physical size of the largest block/sector + on the SPI flash found within the spiffs + usage address space + + spiffs_config.log_block_size - the logical size of a spiffs block + + spiffs_config.log_page_size - the logical size of a spiffs page + +If SPIFFS_SINGLETON is set to one, above parameters must be set ny defines in +the config header file, spiffs_config.h. + + +** Build config + +makefile: The files needed to be compiled to your target resides in files.mk to +be included in your makefile, either by cut and paste or by inclusion. + +Types: spiffs uses the types u8_t, s8_t, u16_t, s16_t, u32_t, s32_t; these must +be typedeffed. + +spiffs_config.h: you also need to define a spiffs_config.h header. Example of +this is found in the default/ directory. + + +** RAM + +Spiffs needs ram. It needs a working buffer being double the size of the +logical page size. It also needs at least one file descriptor. If cache is +enabled (highly recommended), it will also need a bunch of cache pages. + +Say you have a logical page size of 256 bytes. You want to be able to have four +files open simultaneously, and you can give spiffs four cache pages. This +roughly sums up to: + +256*2 (work buffer) + +32*4 (file descriptors) + +(256+32)*4 (cache pages) + 40 (cache metadata) + +i.e. 1832 bytes. + +This is apart from call stack usage. + +To get the exact amount of bytes needed on your specific target, enable +SPIFFS_BUFFER_HELP in spiffs_config.h, rebuild and call: + + SPIFFS_buffer_bytes_for_filedescs + SPIFFS_buffer_bytes_for_cache + +Having these figures you can disable SPIFFS_BUFFER_HELP again to save flash. + + +* HOW TO CONFIG + +TODO \ No newline at end of file diff --git a/app/spiffs/docs/TECH_SPEC b/app/spiffs/docs/TECH_SPEC new file mode 100644 index 00000000..b4755a6d --- /dev/null +++ b/app/spiffs/docs/TECH_SPEC @@ -0,0 +1,239 @@ +* USING SPIFFS + +TODO + + +* SPIFFS DESIGN + +Spiffs is inspired by YAFFS. However, YAFFS is designed for NAND flashes, and +for bigger targets with much more ram. Nevertheless, many wise thoughts have +been borrowed from YAFFS when writing spiffs. Kudos! + +The main complication writing spiffs was that it cannot be assumed the target +has a heap. Spiffs must go along only with the work ram buffer given to it. +This forces extra implementation on many areas of spiffs. + + +** SPI flash devices using NOR technology + +Below is a small description of how SPI flashes work internally. This is to +give an understanding of the design choices made in spiffs. + +SPI flash devices are physically divided in blocks. On some SPI flash devices, +blocks are further divided into sectors. Datasheets sometimes name blocks as +sectors and vice versa. + +Common memory capacaties for SPI flashes are 512kB up to 8MB of data, where +blocks may be 64kB. Sectors can be e.g. 4kB, if supported. Many SPI flashes +have uniform block sizes, whereas others have non-uniform - the latter meaning +that e.g. the first 16 blocks are 4kB big, and the rest are 64kB. + +The entire memory is linear and can be read and written in random access. +Erasing can only be done block- or sectorwise; or by mass erase. + +SPI flashes can normally be erased from 100.000 up to 1.000.000 cycles before +they fail. + +A clean SPI flash from factory have all bits in entire memory set to one. A +mass erase will reset the device to this state. Block or sector erasing will +put the all bits in the area given by the sector or block to ones. Writing to a +NOR flash pulls ones to zeroes. Writing 0xFF to an address is simply a no-op. + +Writing 0b10101010 to a flash address holding 0b00001111 will yield 0b00001010. + +This way of "write by nand" is used considerably in spiffs. + +Common characteristics of NOR flashes are quick reads, but slow writes. + +And finally, unlike NAND flashes, NOR flashes seem to not need any error +correction. They always write correctly I gather. + + +** Spiffs logical structure + +Some terminology before proceeding. Physical blocks/sectors means sizes stated +in the datasheet. Logical blocks and pages is something the integrator choose. + + +** Blocks and pages + +Spiffs is allocated to a part or all of the memory of the SPI flash device. +This area is divided into logical blocks, which in turn are divided into +logical pages. The boundary of a logical block must coincide with one or more +physical blocks. The sizes for logical blocks and logical pages always remain +the same, they are uniform. + +Example: non-uniform flash mapped to spiffs with 128kB logical blocks + +PHYSICAL FLASH BLOCKS SPIFFS LOGICAL BLOCKS: 128kB + ++-----------------------+ - - - +-----------------------+ +| Block 1 : 16kB | | Block 1 : 128kB | ++-----------------------+ | | +| Block 2 : 16kB | | | ++-----------------------+ | | +| Block 3 : 16kB | | | ++-----------------------+ | | +| Block 4 : 16kB | | | ++-----------------------+ | | +| Block 5 : 64kB | | | ++-----------------------+ - - - +-----------------------+ +| Block 6 : 64kB | | Block 2 : 128kB | ++-----------------------+ | | +| Block 7 : 64kB | | | ++-----------------------+ - - - +-----------------------+ +| Block 8 : 64kB | | Block 3 : 128kB | ++-----------------------+ | | +| Block 9 : 64kB | | | ++-----------------------+ - - - +-----------------------+ +| ... | | ... | + +A logical block is divided further into a number of logical pages. A page +defines the smallest data holding element known to spiffs. Hence, if a file +is created being one byte big, it will occupy one page for index and one page +for data - it will occupy 2 x size of a logical page on flash. +So it seems it is good to select a small page size. + +Each page has a metadata header being normally 5 to 9 bytes. This said, a very +small page size will make metadata occupy a lot of the memory on the flash. A +page size of 64 bytes will waste 8-14% on metadata, while 256 bytes 2-4%. +So it seems it is good to select a big page size. + +Also, spiffs uses a ram buffer being two times the page size. This ram buffer +is used for loading and manipulating pages, but it is also used for algorithms +to find free file ids, scanning the file system, etc. Having too small a page +size means less work buffer for spiffs, ending up in more reads operations and +eventually gives a slower file system. + +Choosing the page size for the system involves many factors: + - How big is the logical block size + - What is the normal size of most files + - How much ram can be spent + - How much data (vs metadata) must be crammed into the file system + - How fast must spiffs be + - Other things impossible to find out + +So, chosing the Optimal Page Size (tm) seems tricky, to say the least. Don't +fret - there is no optimal page size. This varies from how the target will use +spiffs. Use the golden rule: + + ~~~ Logical Page Size = Logical Block Size / 256 ~~~ + +This is a good starting point. The final page size can then be derived through +heuristical experimenting for us non-analytical minds. + + +** Objects, indices and look-ups + +A file, or an object as called in spiffs, is identified by an object id. +Another YAFFS rip-off. This object id is a part of the page header. So, all +pages know to which object/file they belong - not counting the free pages. + +An object is made up of two types of pages: object index pages and data pages. +Data pages contain the data written by user. Index pages contain metadata about +the object, more specifically what data pages are part of the object. + +The page header also includes something called a span index. Let's say a file +is written covering three data pages. The first data page will then have span +index 0, the second span index 1, and the last data page will have span index +2. Simple as that. + +Finally, each page header contain flags, telling if the page is used, +deleted, finalized, holds index or data, and more. + +Object indices also have span indices, where an object index with span index 0 +is referred to as the object index header. This page does not only contain +references to data pages, but also extra info such as object name, object size +in bytes, flags for file or directory, etc. + +If one were to create a file covering three data pages, named e.g. +"spandex-joke.txt", given object id 12, it could look like this: + +PAGE 0 + +PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA] + + +PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA] + + +PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA] + + +PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA] + + +PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX] + obj ix header: [name:spandex-joke.txt size:600 bytes flags:FILE] + obj ix: [1 2 4] + +Looking in detail at page 5, the object index header page, the object index +array refers to each data page in order, as mentioned before. The index of the +object index array correlates with the data page span index. + + entry ix: 0 1 2 + obj ix: [1 2 4] + | | | + PAGE 1, DATA, SPAN_IX 0 --------/ | | + PAGE 2, DATA, SPAN_IX 1 --------/ | + PAGE 4, DATA, SPAN_IX 2 --------/ + +Things to be unveiled in page 0 - well.. Spiffs is designed for systems low on +ram. We cannot keep a dynamic list on the whereabouts of each object index +header so we can find a file fast. There might not even be a heap! But, we do +not want to scan all page headers on the flash to find the object index header. + +The first page(s) of each block contains the so called object look-up. These +are not normal pages, they do not have a header. Instead, they are arrays +pointing out what object-id the rest of all pages in the block belongs to. + +By this look-up, only the first page(s) in each block must to scanned to find +the actual page which contains the object index header of the desired object. + +The object lookup is redundant metadata. The assumption is that it presents +less overhead reading a full page of data to memory from each block and search +that, instead of reading a small amount of data from each page (i.e. the page +header) in all blocks. Each read operation from SPI flash normally contains +extra data as the read command itself and the flash address. Also, depending on +the underlying implementation, other criterions may need to be passed for each +read transaction, like mutexes and such. + +The veiled example unveiled would look like this, with some extra pages: + +PAGE 0 [ 12 12 545 12 12 34 34 4 0 0 0 0 ...] +PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA] ... +PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA] ... +PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA] ... +PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA] ... +PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX] ... +PAGE 6 page header: [obj_id:34 span_ix:0 flags:USED|DATA] ... +PAGE 7 page header: [obj_id:34 span_ix:1 flags:USED|DATA] ... +PAGE 8 page header: [obj_id:4 span_ix:1 flags:USED|INDEX] ... +PAGE 9 page header: [obj_id:23 span_ix:0 flags:DELETED|INDEX] ... +PAGE 10 page header: [obj_id:23 span_ix:0 flags:DELETED|DATA] ... +PAGE 11 page header: [obj_id:23 span_ix:1 flags:DELETED|DATA] ... +PAGE 12 page header: [obj_id:23 span_ix:2 flags:DELETED|DATA] ... +... + +Ok, so why are page 9 to 12 marked as 0 when they belong to object id 23? These +pages are deleted, so this is marked both in page header flags and in the look +up. This is an example where spiffs uses NOR flashes "nand-way" of writing. + +As a matter of fact, there are two object id's which are special: + +obj id 0 (all bits zeroes) - indicates a deleted page in object look up +obj id 0xff.. (all bits ones) - indicates a free page in object look up + +Actually, the object id's have another quirk: if the most significant bit is +set, this indicates an object index page. If the most significant bit is zero, +this indicates a data page. So to be fully correct, page 0 in above example +would look like this: + +PAGE 0 [ 12 12 545 12 *12 34 34 *4 0 0 0 0 ...] + +where the asterisk means the msb of the object id is set. + +This is another way to speed up the searches when looking for object indices. +By looking on the object id's msb in the object lookup, it is also possible +to find out whether the page is an object index page or a data page. + diff --git a/app/spiffs/docs/TODO b/app/spiffs/docs/TODO new file mode 100644 index 00000000..88709f02 --- /dev/null +++ b/app/spiffs/docs/TODO @@ -0,0 +1 @@ +* When mending lost pages, also see if they fit into length specified in object index header \ No newline at end of file diff --git a/app/spiffs/params_test.h b/app/spiffs/params_test.h new file mode 100644 index 00000000..38809167 --- /dev/null +++ b/app/spiffs/params_test.h @@ -0,0 +1,36 @@ +/* + * params_test.h + * + * Created on: May 26, 2013 + * Author: petera + */ + +#ifndef PARAMS_TEST_H_ +#define PARAMS_TEST_H_ + +// // total emulated spi flash size +// #define PHYS_FLASH_SIZE (16*1024*1024) +// // spiffs file system size +// #define SPIFFS_FLASH_SIZE (2*1024*1024) +// // spiffs file system offset in emulated spi flash +// #define SPIFFS_PHYS_ADDR (4*1024*1024) + +// #define SECTOR_SIZE 65536 +// #define LOG_BLOCK (SECTOR_SIZE*2) +// #define LOG_PAGE (SECTOR_SIZE/256) + +// #define FD_BUF_SIZE 64*6 +// #define CACHE_BUF_SIZE (LOG_PAGE + 32)*8 + +// #define ASSERT(c, m) real_assert((c),(m), __FILE__, __LINE__); + +typedef signed int s32_t; +typedef unsigned int u32_t; +typedef signed short s16_t; +typedef unsigned short u16_t; +typedef signed char s8_t; +typedef unsigned char u8_t; + +void real_assert(int c, const char *n, const char *file, int l); + +#endif /* PARAMS_TEST_H_ */ diff --git a/app/spiffs/spiffs.c b/app/spiffs/spiffs.c new file mode 100644 index 00000000..c4fa2b14 --- /dev/null +++ b/app/spiffs/spiffs.c @@ -0,0 +1,159 @@ +#include "c_stdio.h" +#include "platform.h" +#include "spiffs.h" + +spiffs fs; + +#define LOG_PAGE_SIZE 256 + +static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2]; +static u8_t spiffs_fds[32*4]; +static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*4]; + +static s32_t ICACHE_FLASH_ATTR my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) { + platform_flash_read(dst, addr, size); + return SPIFFS_OK; +} + +static s32_t ICACHE_FLASH_ATTR my_spiffs_write(u32_t addr, u32_t size, u8_t *src) { + platform_flash_write(src, addr, size); + return SPIFFS_OK; +} + +static s32_t ICACHE_FLASH_ATTR my_spiffs_erase(u32_t addr, u32_t size) { + u32_t sect_first = platform_flash_get_sector_of_address(addr); + u32_t sect_last = sect_first; + while( sect_first <= sect_last ) + if( platform_flash_erase_sector( sect_first ++ ) == PLATFORM_ERR ) + return SPIFFS_ERR_INTERNAL; + return SPIFFS_OK; +} + +void myspiffs_check_callback(spiffs_check_type type, spiffs_check_report report, u32_t arg1, u32_t arg2){ + // if(SPIFFS_CHECK_PROGRESS == report) return; + // NODE_ERR("type: %d, report: %d, arg1: %d, arg2: %d\n", type, report, arg1, arg2); +} + +/******************* +The W25Q32BV array is organized into 16,384 programmable pages of 256-bytes each. Up to 256 bytes can be programmed at a time.  +Pages can be erased in groups of 16 (4KB sector erase), groups of 128 (32KB block erase), groups of 256 (64KB block erase) or  +the entire chip (chip erase). The W25Q32BV has 1,024 erasable sectors and 64 erasable blocks respectively.  +The small 4KB sectors allow for greater flexibility in applications that require data and parameter storage.  + +********************/ + +void ICACHE_FLASH_ATTR spiffs_mount() { + spiffs_config cfg; + cfg.phys_addr = ( u32_t )platform_flash_get_first_free_block_address( NULL ); + cfg.phys_size = INTERNAL_FLASH_SIZE - ( ( u32_t )cfg.phys_addr - INTERNAL_FLASH_START_ADDRESS ); + cfg.phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE; // according to datasheet + cfg.log_block_size = INTERNAL_FLASH_SECTOR_SIZE; // let us not complicate things + cfg.log_page_size = LOG_PAGE_SIZE; // as we said + NODE_DBG("fs.start:%x,max:%x\n",cfg.phys_addr,cfg.phys_size); + + cfg.hal_read_f = my_spiffs_read; + cfg.hal_write_f = my_spiffs_write; + cfg.hal_erase_f = my_spiffs_erase; + + int res = SPIFFS_mount(&fs, + &cfg, + spiffs_work_buf, + spiffs_fds, + sizeof(spiffs_fds), + spiffs_cache, + sizeof(spiffs_cache), + // myspiffs_check_callback); + 0); + NODE_DBG("mount res: %i\n", res); +} + +// FS formatting function +// Returns 1 if OK, 0 for error +int ICACHE_FLASH_ATTR myspiffs_format( void ) +{ + SPIFFS_unmount(&fs); + u32_t sect_first, sect_last; + sect_first = ( u32_t )platform_flash_get_first_free_block_address( NULL ); + sect_first = platform_flash_get_sector_of_address(sect_first); + sect_last = INTERNAL_FLASH_SIZE + INTERNAL_FLASH_START_ADDRESS - 4; + sect_last = platform_flash_get_sector_of_address(sect_last); + NODE_DBG("sect_first: %x, sect_last: %x\n", sect_first, sect_last); + while( sect_first <= sect_last ) + if( platform_flash_erase_sector( sect_first ++ ) == PLATFORM_ERR ) + return 0; + return 1; +} + +int ICACHE_FLASH_ATTR myspiffs_check( void ) +{ + // ets_wdt_disable(); + // int res = (int)SPIFFS_check(&fs); + // ets_wdt_enable(); + // return res; +} + +int ICACHE_FLASH_ATTR myspiffs_open(const char *name, int flags){ + return (int)SPIFFS_open(&fs, name, (spiffs_flags)flags, 0); +} + +int ICACHE_FLASH_ATTR myspiffs_close( int fd ){ + SPIFFS_close(&fs, (spiffs_file)fd); + return 0; +} +size_t ICACHE_FLASH_ATTR myspiffs_write( int fd, const void* ptr, size_t len ){ +#if 0 + if(fd==c_stdout || fd==c_stderr){ + uart0_tx_buffer((u8_t*)ptr, len); + return len; + } +#endif + return SPIFFS_write(&fs, (spiffs_file)fd, (void *)ptr, len); +} +size_t ICACHE_FLASH_ATTR myspiffs_read( int fd, void* ptr, size_t len){ + return SPIFFS_read(&fs, (spiffs_file)fd, ptr, len); +} +int ICACHE_FLASH_ATTR myspiffs_lseek( int fd, int off, int whence ){ + return SPIFFS_lseek(&fs, (spiffs_file)fd, off, whence); +} +int ICACHE_FLASH_ATTR myspiffs_eof( int fd ){ + return SPIFFS_eof(&fs, (spiffs_file)fd); +} +int ICACHE_FLASH_ATTR myspiffs_tell( int fd ){ + return SPIFFS_tell(&fs, (spiffs_file)fd); +} +int ICACHE_FLASH_ATTR myspiffs_getc( int fd ){ + char c = EOF; + if(!myspiffs_eof(fd)){ + SPIFFS_read(&fs, (spiffs_file)fd, &c, 1); + } + return (int)c; +} +int ICACHE_FLASH_ATTR myspiffs_ungetc( int c, int fd ){ + return SPIFFS_lseek(&fs, (spiffs_file)fd, -1, SEEK_CUR); +} +int ICACHE_FLASH_ATTR myspiffs_flush( int fd ){ + return SPIFFS_fflush(&fs, (spiffs_file)fd); +} +int ICACHE_FLASH_ATTR myspiffs_error( int fd ){ + return SPIFFS_errno(&fs); +} +void ICACHE_FLASH_ATTR myspiffs_clearerr( int fd ){ + fs.errno = SPIFFS_OK; +} +#if 0 +void ICACHE_FLASH_ATTR test_spiffs() { + char buf[12]; + + // Surely, I've mounted spiffs before entering here + + spiffs_file fd = SPIFFS_open(&fs, "my_file", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0); + if (SPIFFS_write(&fs, fd, (u8_t *)"Hello world", 12) < 0) NODE_DBG("errno %i\n", SPIFFS_errno(&fs)); + SPIFFS_close(&fs, fd); + + fd = SPIFFS_open(&fs, "my_file", SPIFFS_RDWR, 0); + if (SPIFFS_read(&fs, fd, (u8_t *)buf, 12) < 0) NODE_DBG("errno %i\n", SPIFFS_errno(&fs)); + SPIFFS_close(&fs, fd); + + NODE_DBG("--> %s <--\n", buf); +} +#endif diff --git a/app/spiffs/spiffs.h b/app/spiffs/spiffs.h new file mode 100644 index 00000000..9875590c --- /dev/null +++ b/app/spiffs/spiffs.h @@ -0,0 +1,442 @@ +/* + * spiffs.h + * + * Created on: May 26, 2013 + * Author: petera + */ + + + +#ifndef SPIFFS_H_ +#define SPIFFS_H_ +#include "c_stdio.h" +#include "spiffs_config.h" + +#define SPIFFS_OK 0 +#define SPIFFS_ERR_NOT_MOUNTED -10000 +#define SPIFFS_ERR_FULL -10001 +#define SPIFFS_ERR_NOT_FOUND -10002 +#define SPIFFS_ERR_END_OF_OBJECT -10003 +#define SPIFFS_ERR_DELETED -10004 +#define SPIFFS_ERR_NOT_FINALIZED -10005 +#define SPIFFS_ERR_NOT_INDEX -10006 +#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007 +#define SPIFFS_ERR_FILE_CLOSED -10008 +#define SPIFFS_ERR_FILE_DELETED -10009 +#define SPIFFS_ERR_BAD_DESCRIPTOR -10010 +#define SPIFFS_ERR_IS_INDEX -10011 +#define SPIFFS_ERR_IS_FREE -10012 +#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013 +#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014 +#define SPIFFS_ERR_INDEX_REF_FREE -10015 +#define SPIFFS_ERR_INDEX_REF_LU -10016 +#define SPIFFS_ERR_INDEX_REF_INVALID -10017 +#define SPIFFS_ERR_INDEX_FREE -10018 +#define SPIFFS_ERR_INDEX_LU -10019 +#define SPIFFS_ERR_INDEX_INVALID -10020 +#define SPIFFS_ERR_NOT_WRITABLE -10021 +#define SPIFFS_ERR_NOT_READABLE -10022 + +#define SPIFFS_ERR_INTERNAL -10050 + +#define SPIFFS_ERR_TEST -10100 + + +// spiffs file descriptor index type. must be signed +typedef s16_t spiffs_file; +// spiffs file descriptor flags +typedef u16_t spiffs_flags; +// spiffs file mode +typedef u16_t spiffs_mode; +// object type +typedef u8_t spiffs_obj_type; + +/* spi read call function type */ +typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst); +/* spi write call function type */ +typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src); +/* spi erase call function type */ +typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); + +/* file system check callback report operation */ +typedef enum { + SPIFFS_CHECK_LOOKUP = 0, + SPIFFS_CHECK_INDEX, + SPIFFS_CHECK_PAGE +} spiffs_check_type; + +/* file system check callback report type */ +typedef enum { + SPIFFS_CHECK_PROGRESS = 0, + SPIFFS_CHECK_ERROR, + SPIFFS_CHECK_FIX_INDEX, + SPIFFS_CHECK_FIX_LOOKUP, + SPIFFS_CHECK_DELETE_ORPHANED_INDEX, + SPIFFS_CHECK_DELETE_PAGE, + SPIFFS_CHECK_DELETE_BAD_FILE, +} spiffs_check_report; + +/* file system check callback function */ +typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report, + u32_t arg1, u32_t arg2); + +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(...) \ + print(__VA_ARGS__) +#endif +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__) +#endif +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__) +#endif +#ifndef SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__) +#endif + +/* Any write to the filehandle is appended to end of the file */ +#define SPIFFS_APPEND (1<<0) +/* If the opened file exists, it will be truncated to zero length before opened */ +#define SPIFFS_TRUNC (1<<1) +/* If the opened file does not exist, it will be created before opened */ +#define SPIFFS_CREAT (1<<2) +/* The opened file may only be read */ +#define SPIFFS_RDONLY (1<<3) +/* The opened file may only be writted */ +#define SPIFFS_WRONLY (1<<4) +/* The opened file may be both read and writted */ +#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY) +/* Any writes to the filehandle will never be cached */ +#define SPIFFS_DIRECT (1<<5) + +#define SPIFFS_SEEK_SET (0) +#define SPIFFS_SEEK_CUR (1) +#define SPIFFS_SEEK_END (2) + +#define SPIFFS_TYPE_FILE (1) +#define SPIFFS_TYPE_DIR (2) +#define SPIFFS_TYPE_HARD_LINK (3) +#define SPIFFS_TYPE_SOFT_LINK (4) + +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) +#endif + +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) +#endif + +// phys structs + +// spiffs spi configuration struct +typedef struct { + // physical read function + spiffs_read hal_read_f; + // physical write function + spiffs_write hal_write_f; + // physical erase function + spiffs_erase hal_erase_f; +#if SPIFFS_SINGLETON == 0 + // physical size of the spi flash + u32_t phys_size; + // physical offset in spi flash used for spiffs, + // must be on block boundary + u32_t phys_addr; + // physical size when erasing a block + u32_t phys_erase_block; + + // logical size of a block, must be on physical + // block size boundary and must never be less than + // a physical block + u32_t log_block_size; + // logical size of a page, must be at least + // log_block_size / 8 + u32_t log_page_size; +#endif +} spiffs_config; + +typedef struct { + // file system configuration + spiffs_config cfg; + // number of logical blocks + u32_t block_count; + + // cursor for free blocks, block index + spiffs_block_ix free_cursor_block_ix; + // cursor for free blocks, entry index + int free_cursor_obj_lu_entry; + // cursor when searching, block index + spiffs_block_ix cursor_block_ix; + // cursor when searching, entry index + int cursor_obj_lu_entry; + + // primary work buffer, size of a logical page + u8_t *lu_work; + // secondary work buffer, size of a logical page + u8_t *work; + // file descriptor memory area + u8_t *fd_space; + // available file descriptors + u32_t fd_count; + + // last error + s32_t errno; + + // current number of free blocks + u32_t free_blocks; + // current number of busy pages + u32_t stats_p_allocated; + // current number of deleted pages + u32_t stats_p_deleted; + // flag indicating that garbage collector is cleaning + u8_t cleaning; + // max erase count amongst all blocks + spiffs_obj_id max_erase_count; + +#if SPIFFS_GC_STATS + u32_t stats_gc_runs; +#endif + +#if SPIFFS_CACHE + // cache memory + void *cache; + // cache size + u32_t cache_size; +#if SPIFFS_CACHE_STATS + u32_t cache_hits; + u32_t cache_misses; +#endif +#endif + + // check callback function + spiffs_check_callback check_cb_f; +} spiffs; + +/* spiffs file status struct */ +typedef struct { + spiffs_obj_id obj_id; + u32_t size; + spiffs_obj_type type; + u8_t name[SPIFFS_OBJ_NAME_LEN]; +} spiffs_stat; + +struct spiffs_dirent { + spiffs_obj_id obj_id; + u8_t name[SPIFFS_OBJ_NAME_LEN]; + spiffs_obj_type type; + u32_t size; +}; + +typedef struct { + spiffs *fs; + spiffs_block_ix block; + int entry; +} spiffs_DIR; + +// functions + +/** + * Initializes the file system dynamic parameters and mounts the filesystem + * @param fs the file system struct + * @param config the physical and logical configuration of the file system + * @param work a memory work buffer comprising 2*config->log_page_size + * bytes used throughout all file system operations + * @param fd_space memory for file descriptors + * @param fd_space_size memory size of file descriptors + * @param cache memory for cache, may be null + * @param cache_size memory size of cache + * @param check_cb_f callback function for reporting during consistency checks + */ +s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size, + spiffs_check_callback check_cb_f); + +/** + * Unmounts the file system. All file handles will be flushed of any + * cached writes and closed. + * @param fs the file system struct + */ +void SPIFFS_unmount(spiffs *fs); + +/** + * Creates a new file. + * @param fs the file system struct + * @param path the path of the new file + * @param mode ignored, for posix compliance + */ +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode); + +/** + * Opens/creates a file. + * @param fs the file system struct + * @param path the path of the new file + * @param flags the flags for the open command, can be combinations of + * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, + * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT + * @param mode ignored, for posix compliance + */ +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode); + +/** + * Reads from given filehandle. + * @param fs the file system struct + * @param fh the filehandle + * @param buf where to put read data + * @param len how much to read + * @returns number of bytes read, or -1 if error + */ +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, u32_t len); + +/** + * Writes to given filehandle. + * @param fs the file system struct + * @param fh the filehandle + * @param buf the data to write + * @param len how much to write + * @returns number of bytes written, or -1 if error + */ +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len); + +/** + * Moves the read/write file offset + * @param fs the file system struct + * @param fh the filehandle + * @param offs how much/where to move the offset + * @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes + * if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset + * if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offset + */ +s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence); + +/** + * Removes a file by path + * @param fs the file system struct + * @param path the path of the file to remove + */ +s32_t SPIFFS_remove(spiffs *fs, const char *path); + +/** + * Removes a file by filehandle + * @param fs the file system struct + * @param fh the filehandle of the file to remove + */ +s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh); + +/** + * Gets file status by path + * @param fs the file system struct + * @param path the path of the file to stat + * @param s the stat struct to populate + */ +s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s); + +/** + * Gets file status by filehandle + * @param fs the file system struct + * @param fh the filehandle of the file to stat + * @param s the stat struct to populate + */ +s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s); + +/** + * Flushes all pending write operations from cache for given file + * @param fs the file system struct + * @param fh the filehandle of the file to flush + */ +s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh); + +/** + * Closes a filehandle. If there are pending write operations, these are finalized before closing. + * @param fs the file system struct + * @param fh the filehandle of the file to close + */ +void SPIFFS_close(spiffs *fs, spiffs_file fh); + +/** + * Returns last error of last file operation. + * @param fs the file system struct + */ +s32_t SPIFFS_errno(spiffs *fs); + +/** + * Opens a directory stream corresponding to the given name. + * The stream is positioned at the first entry in the directory. + * On hydrogen builds the name argument is ignored as hydrogen builds always correspond + * to a flat file structure - no directories. + * @param fs the file system struct + * @param name the name of the directory + * @param d pointer the directory stream to be populated + */ +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d); + +/** + * Closes a directory stream + * @param d the directory stream to close + */ +s32_t SPIFFS_closedir(spiffs_DIR *d); + +/** + * Reads a directory into given spifs_dirent struct. + * @param d pointer to the directory stream + * @param e the dirent struct to be populated + * @returns null if error or end of stream, else given dirent is returned + */ +struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); + +/** + * Runs a consistency check on given filesystem. + * @param fs the file system struct + */ +s32_t SPIFFS_check(spiffs *fs); + +/** + * Check if EOF reached. + * @param fs the file system struct + * @param fh the filehandle of the file to check + */ +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); +s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); + +#if SPIFFS_TEST_VISUALISATION +/** + * Prints out a visualization of the filesystem. + * @param fs the file system struct + */ +s32_t SPIFFS_vis(spiffs *fs); +#endif + +#if SPIFFS_BUFFER_HELP +/** + * Returns number of bytes needed for the filedescriptor buffer given + * amount of file descriptors. + */ +u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs); + +#if SPIFFS_CACHE +/** + * Returns number of bytes needed for the cache buffer given + * amount of cache pages. + */ +u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages); +#endif +#endif + +#if SPIFFS_CACHE +#endif + +int myspiffs_open(const char *name, int flags); +int myspiffs_close( int fd ); +size_t myspiffs_write( int fd, const void* ptr, size_t len ); +size_t myspiffs_read( int fd, void* ptr, size_t len); +int myspiffs_lseek( int fd, int off, int whence ); +int myspiffs_eof( int fd ); +int myspiffs_tell( int fd ); +int myspiffs_getc( int fd ); +int myspiffs_ungetc( int c, int fd ); +int myspiffs_flush( int fd ); +int myspiffs_error( int fd ); +void myspiffs_clearerr( int fd ); +int myspiffs_check( void ); + +#endif /* SPIFFS_H_ */ diff --git a/app/spiffs/spiffs_cache.c b/app/spiffs/spiffs_cache.c new file mode 100644 index 00000000..d56823cc --- /dev/null +++ b/app/spiffs/spiffs_cache.c @@ -0,0 +1,301 @@ +/* + * spiffs_cache.c + * + * Created on: Jun 23, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if SPIFFS_CACHE + +// returns cached page for give page index, or null if no such cached page +static spiffs_cache_page *ICACHE_FLASH_ATTR spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) { + spiffs_cache *cache = spiffs_get_cache(fs); + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0; + int i; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + cp->pix == pix ) { + SPIFFS_CACHE_DBG("CACHE_GET: have cache page %i for %04x\n", i, pix); + cp->last_access = cache->last_access; + return cp; + } + } + //SPIFFS_CACHE_DBG("CACHE_GET: no cache for %04x\n", pix); + return 0; +} + +// frees cached page +static s32_t ICACHE_FLASH_ATTR spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix); + if (cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) { + u8_t *mem = spiffs_get_cache_page(fs, cache, ix); + res = fs->cfg.hal_write_f(SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); + } + + cp->flags = 0; + cache->cpage_use_map &= ~(1 << ix); + + if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i objid %04x\n", ix, cp->obj_id); + } else { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i pix %04x\n", ix, cp->pix); + } + } + + return res; +} + +// removes the oldest accessed cached page +static s32_t ICACHE_FLASH_ATTR spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + + if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) { + // at least one free cpage + return SPIFFS_OK; + } + + // all busy, scan thru all to find the cpage which has oldest access + int i; + int cand_ix = -1; + u32_t oldest_val = 0; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->last_access - cp->last_access) > oldest_val && + (cp->flags & flag_mask) == flags) { + oldest_val = cache->last_access - cp->last_access; + cand_ix = i; + } + } + + if (cand_ix >= 0) { + res = spiffs_cache_page_free(fs, cand_ix, 1); + } + + return res; +} + +// allocates a new cached page and returns it, or null if all cache pages are busy +static spiffs_cache_page *ICACHE_FLASH_ATTR spiffs_cache_page_allocate(spiffs *fs) { + spiffs_cache *cache = spiffs_get_cache(fs); + if (cache->cpage_use_map == 0xffffffff) { + // out of cache memory + return 0; + } + int i; + for (i = 0; i < cache->cpage_count; i++) { + if ((cache->cpage_use_map & (1<cpage_use_map |= (1<last_access = cache->last_access; + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page %i\n", i); + return cp; + } + } + // out of cache entries + return 0; +} + +// drops the cache page for give page index +void ICACHE_FLASH_ATTR spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) { + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + if (cp) { + spiffs_cache_page_free(fs, cp->ix, 0); + } +} + +// ------------------------------ + +// reads from spi flash or the cache +s32_t ICACHE_FLASH_ATTR spiffs_phys_rd( + spiffs *fs, + u8_t op, + spiffs_file fh, + u32_t addr, + u32_t len, + u8_t *dst) { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr)); + cache->last_access++; + if (cp) { +#if SPIFFS_CACHE_STATS + fs->cache_hits++; +#endif + cp->last_access = cache->last_access; + } else { + if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) { + // for second layer lookup functions, we do not cache in order to prevent shredding + return fs->cfg.hal_read_f( + addr , + len, + dst); + } +#if SPIFFS_CACHE_STATS + fs->cache_misses++; +#endif + res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + cp = spiffs_cache_page_allocate(fs); + if (cp) { + cp->flags = SPIFFS_CACHE_FLAG_WRTHRU; + cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + } + + s32_t res2 = fs->cfg.hal_read_f( + addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + spiffs_get_cache_page(fs, cache, cp->ix)); + if (res2 != SPIFFS_OK) { + res = res2; + } + } + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + c_memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + return res; +} + +// writes to spi flash and/or the cache +s32_t ICACHE_FLASH_ATTR spiffs_phys_wr( + spiffs *fs, + u8_t op, + spiffs_file fh, + u32_t addr, + u32_t len, + u8_t *src) { + spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + + if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) { + // have a cache page + // copy in data to cache page + + if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE && + (op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) { + // page is being deleted, wipe from cache - unless it is a lookup page + spiffs_cache_page_free(fs, cp->ix, 0); + return fs->cfg.hal_write_f(addr, len, src); + } + + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + c_memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); + + cache->last_access++; + cp->last_access = cache->last_access; + + if (cp->flags && SPIFFS_CACHE_FLAG_WRTHRU) { + // page is being updated, no write-cache, just pass thru + return fs->cfg.hal_write_f(addr, len, src); + } else { + return SPIFFS_OK; + } + } else { + // no cache page, no write cache - just write thru + return fs->cfg.hal_write_f(addr, len, src); + } +} + +#if SPIFFS_CACHE_WR +// returns the cache page that this fd refers, or null if no cache page +spiffs_cache_page *ICACHE_FLASH_ATTR spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) { + spiffs_cache *cache = spiffs_get_cache(fs); + + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) { + // all cpages free, no cpage cannot be assigned to obj_id + return 0; + } + + int i; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) && + cp->obj_id == fd->obj_id) { + return cp; + } + } + + return 0; +} + +// allocates a new cache page and refers this to given fd - flushes an old cache +// page if all cache is busy +spiffs_cache_page *ICACHE_FLASH_ATTR spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) { + // before this function is called, it is ensured that there is no already existing + // cache page with same object id + spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + spiffs_cache_page *cp = spiffs_cache_page_allocate(fs); + if (cp == 0) { + // could not get cache page + return 0; + } + + cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR; + cp->obj_id = fd->obj_id; + fd->cache_page = cp; + return cp; +} + +// unrefers all fds that this cache page refers to and releases the cache page +void ICACHE_FLASH_ATTR spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) { + if (cp == 0) return; + int i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) { + cur_fd->cache_page = 0; + } + } + spiffs_cache_page_free(fs, cp->ix, 0); + + cp->obj_id = 0; +} + +#endif + +// initializes the cache +void ICACHE_FLASH_ATTR spiffs_cache_init(spiffs *fs) { + if (fs->cache == 0) return; + u32_t sz = fs->cache_size; + u32_t cache_mask = 0; + int i; + int cache_entries = + (sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs)); + if (cache_entries <= 0) return; + + for (i = 0; i < cache_entries; i++) { + cache_mask <<= 1; + cache_mask |= 1; + } + + spiffs_cache cache; + c_memset(&cache, 0, sizeof(spiffs_cache)); + cache.cpage_count = cache_entries; + cache.cpages = (u8_t *)(fs->cache) + sizeof(spiffs_cache); + + cache.cpage_use_map = 0xffffffff; + cache.cpage_use_mask = cache_mask; + c_memcpy(fs->cache, &cache, sizeof(spiffs_cache)); + + spiffs_cache *c = spiffs_get_cache(fs); + + c_memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs)); + + c->cpage_use_map &= ~(c->cpage_use_mask); + for (i = 0; i < cache.cpage_count; i++) { + spiffs_get_cache_page_hdr(fs, c, i)->ix = i; + } +} + +#endif // SPIFFS_CACHE diff --git a/app/spiffs/spiffs_check.c b/app/spiffs/spiffs_check.c new file mode 100644 index 00000000..f67b9652 --- /dev/null +++ b/app/spiffs/spiffs_check.c @@ -0,0 +1,967 @@ +/* + * spiffs_check.c + * + * Contains functionality for checking file system consistency + * and mending problems. + * Three levels of consistency checks are implemented: + * + * Look up consistency + * Checks if indices in lookup pages are coherent with page headers + * Object index consistency + * Checks if there are any orphaned object indices (missing object index headers). + * If an object index is found but not its header, the object index is deleted. + * This is critical for the following page consistency check. + * Page consistency + * Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed + * + * + * Created on: Jul 7, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +//--------------------------------------- +// Look up consistency + +// searches in the object indices and returns the referenced page index given +// the object id and the data span index +// destroys fs->lu_work +static s32_t ICACHE_FLASH_ATTR spiffs_object_get_data_page_index_reference( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix data_spix, + spiffs_page_ix *pix, + spiffs_page_ix *objix_pix) { + s32_t res; + + // calculate object index span index for given data page span index + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // find obj index for obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix); + SPIFFS_CHECK_RES(res); + + // load obj index entry + u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix); + if (objix_spix == 0) { + // get referenced page from object index header + addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix); + } else { + // get referenced page from object index + addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix); + } + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix); + + return res; +} + +// copies page contents to a new page +static s32_t ICACHE_FLASH_ATTR spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) { + s32_t res; + res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0,0,0,0, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + return res; +} + +// rewrites the object index for given object id and replaces the +// data page index to a new page index +static s32_t ICACHE_FLASH_ATTR spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + + // calculate object index span index for given data page span index + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (objix_spix == 0) { + // calc index in index header + entry = data_spix; + } else { + // calc entry in index + entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix); + } + // load index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; + + // be ultra safe, double check header against provided data + if (objix_p_hdr->obj_id != obj_id) { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_OBJ_ID_MISM; + } + if (objix_p_hdr->span_ix != objix_spix) { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_SPIX_MISM; + } + if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX | + SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) != + (SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_FLAGS_BAD; + } + + // rewrite in mem + if (objix_spix == 0) { + ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + } else { + ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + } + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + res = spiffs_page_delete(fs, objix_pix); + + return res; +} + +// deletes an object just by marking object index header as deleted +static s32_t ICACHE_FLASH_ATTR spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) { + spiffs_page_ix objix_hdr_pix; + s32_t res; + res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + return SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&flags); + return res; +} + +// validates the given look up entry +static s32_t ICACHE_FLASH_ATTR spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr, + spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) { + u8_t delete_page = 0; + s32_t res = SPIFFS_OK; + spiffs_page_ix objix_pix; + spiffs_page_ix ref_pix; + // check validity, take actions + if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) || + ((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) { + // look up entry deleted / free but used in page header + SPIFFS_CHECK_DBG("LU: pix %04x deleted/free in lu but not on page\n", cur_pix); + *reload_lu = 1; + delete_page = 1; + if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { + // header says data page + // data page can be removed if not referenced by some object index + res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no object with this id, so remove page safely + res = SPIFFS_OK; + } else { + SPIFFS_CHECK_RES(res); + if (ref_pix == cur_pix) { + // data page referenced by object index but deleted in lu + // copy page to new place and re-write the object index to new place + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting %04x to new page %04x\n", cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + SPIFFS_CHECK_DBG("LU: FIXUP: %04x rewritten to %04x, affected objix_pix %04x\n", cur_pix, new_pix, objix_pix); + res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("LU: FIXUP: index bad %i, cannot mend!\n", res); + res = spiffs_page_delete(fs, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + } else { + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix); + } + SPIFFS_CHECK_RES(res); + } + } + } else { + // header says index page + // index page can be removed if other index with same obj_id and spanix is found + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no such index page found, check for a data page amongst page headers + // lu cannot be trusted + res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0); + if (res == SPIFFS_OK) { // ignore other errors + // got a data page also, assume lu corruption only, rewrite to new page + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting %04x to new page %04x\n", cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + } + } else { + SPIFFS_CHECK_RES(res); + } + } + } + if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) { + // look up entry used + if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) { + SPIFFS_CHECK_DBG("LU: pix %04x differ in obj_id lu:%04x ph:%04x\n", cur_pix, lu_obj_id, p_hdr->obj_id); + delete_page = 1; + if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 || + (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) || + (p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) { + // page deleted or not finalized, just remove it + } else { + if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { + // if data page, check for reference to this page + res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no object with this id, so remove page safely + res = SPIFFS_OK; + } else { + SPIFFS_CHECK_RES(res); + // if found, rewrite page with object id, update index, and delete current + if (ref_pix == cur_pix) { + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("LU: FIXUP: index bad %i, cannot mend!\n", res); + res = spiffs_page_delete(fs, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); + *reload_lu = 1; + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + } + SPIFFS_CHECK_RES(res); + } + } + } else { + // else if index, check for other pages with both obj_id's and spanix + spiffs_page_ix objix_pix_lu, objix_pix_ph; + // see if other object index page exists for lookup obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_lu); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_lu = 0; + } + SPIFFS_CHECK_RES(res); + // see if other object index exists for page header obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_ph = 0; + } + SPIFFS_CHECK_RES(res); + // if both obj_id's found, just delete current + if (objix_pix_ph == 0 || objix_pix_lu == 0) { + // otherwise try finding first corresponding data pages + spiffs_page_ix data_pix_lu, data_pix_ph; + // see if other data page exists for look up obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_lu = 0; + } + SPIFFS_CHECK_RES(res); + // see if other data page exists for page header obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_ph = 0; + } + SPIFFS_CHECK_RES(res); + + spiffs_page_header new_ph; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL); + new_ph.span_ix = p_hdr->span_ix; + spiffs_page_ix new_pix; + if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) || + (objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) { + // got a data page for page header obj id + // rewrite as obj_id_ph + new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page %04x as %04x to pix %04x\n", cur_pix, new_ph.obj_id, new_pix); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + } else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) || + (objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) { + // got a data page for look up obj id + // rewrite as obj_id_lu + new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; + SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page %04x as %04x\n", cur_pix, new_ph.obj_id); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + } else { + // cannot safely do anything + SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n"); + } + } + } + } + } else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) || + ((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) { + SPIFFS_CHECK_DBG("LU: %04x lu/page index marking differ\n", cur_pix); + spiffs_page_ix data_pix, objix_pix; + // see if other data page exists for given obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + // see if other object index exists for given obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix = 0; + } + SPIFFS_CHECK_RES(res); + + delete_page = 1; + // if other data page exists and object index exists, just delete page + if (data_pix && objix_pix) { + SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n"); + } else + // if only data page exists, make this page index + if (data_pix && objix_pix == 0) { + SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n"); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix); + spiffs_page_header new_ph; + spiffs_page_ix new_pix; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = p_hdr->span_ix; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); + SPIFFS_CHECK_RES(res); + } else + // if only index exists, make data page + if (data_pix == 0 && objix_pix) { + SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n"); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix); + spiffs_page_header new_ph; + spiffs_page_ix new_pix; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); + new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = p_hdr->span_ix; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); + SPIFFS_CHECK_RES(res); + } else { + // if nothing exists, we cannot safely make a decision - delete + } + } + else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) { + SPIFFS_CHECK_DBG("LU: pix %04x busy in lu but deleted on page\n", cur_pix); + delete_page = 1; + } else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) { + SPIFFS_CHECK_DBG("LU: pix %04x busy but not final\n", cur_pix); + // page can be removed if not referenced by object index + *reload_lu = 1; + res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no object with this id, so remove page safely + res = SPIFFS_OK; + delete_page = 1; + } else { + SPIFFS_CHECK_RES(res); + if (ref_pix != cur_pix) { + SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n"); + delete_page = 1; + } else { + // page referenced by object index but not final + // just finalize + SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n"); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), (u8_t*)&flags); + } + } + } + } + + if (delete_page) { + SPIFFS_CHECK_DBG("LU: FIXUP: deleting page %04x\n", cur_pix); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + } + + return res; +} + +static s32_t ICACHE_FLASH_ATTR spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry, + u32_t user_data, void *user_p) { + s32_t res = SPIFFS_OK; + spiffs_page_header p_hdr; + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); + + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, + (cur_block * 256)/fs->block_count, 0); + + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + int reload_lu = 0; + + res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu); + SPIFFS_CHECK_RES(res); + + if (res == SPIFFS_OK) { + return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE; + } + return res; +} + + +// Scans all object look up. For each entry, corresponding page header is checked for validity. +// If an object index header page is found, this is also checked +s32_t ICACHE_FLASH_ATTR spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { + s32_t res = SPIFFS_OK; + + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); + + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + if (res != SPIFFS_OK) { + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); + } + + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); + + return res; +} + +//--------------------------------------- +// Page consistency + +// Scans all pages (except lu pages), reserves 4 bits in working memory for each page +// bit 0: 0 == FREE|DELETED, 1 == USED +// bit 1: 0 == UNREFERENCED, 1 == REFERENCED +// bit 2: 0 == NOT_INDEX, 1 == INDEX +// bit 3: unused +// A consistent file system will have only pages being +// * x000 free, unreferenced, not index +// * x011 used, referenced only once, not index +// * x101 used, unreferenced, index +// The working memory might not fit all pages so several scans might be needed +static s32_t ICACHE_FLASH_ATTR spiffs_page_consistency_check_i(spiffs *fs) { + const u32_t bits = 4; + const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits; + + s32_t res = SPIFFS_OK; + spiffs_page_ix pix_offset = 0; + + // for each range of pages fitting into work memory + while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) { + // set this flag to abort all checks and rescan the page range + u8_t restart = 0; + c_memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + + spiffs_block_ix cur_block = 0; + // build consistency bitmap for id range traversing all blocks + while (!restart && cur_block < fs->block_count) { + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, + (pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) + + ((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count), + 0); + + // traverse each page except for lookup pages + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block; + while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) { + // read header + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan); + const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8/bits); + const u8_t pix_bit_ix = (cur_pix & ((8/bits)-1)) * bits; + + if (within_range && + (p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) { + // used + fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 0)); + } + if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) && + (p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) && + (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) { + // found non-deleted index + if (within_range) { + fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 2)); + } + + // load non-deleted index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + + // traverse index for referenced pages + spiffs_page_ix *object_page_index; + spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; + + int entries; + int i; + spiffs_span_ix data_spix_offset; + if (p_hdr.span_ix == 0) { + // object header page index + entries = SPIFFS_OBJ_HDR_IX_LEN(fs); + data_spix_offset = 0; + object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)); + } else { + // object page index + entries = SPIFFS_OBJ_IX_LEN(fs); + data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1); + object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)); + } + + // for all entries in index + for (i = 0; !restart && i < entries; i++) { + spiffs_page_ix rpix = object_page_index[i]; + u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan; + + if ((rpix != (spiffs_page_ix)-1 && rpix > SPIFFS_MAX_PAGES(fs)) + || (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) { + + // bad reference + SPIFFS_CHECK_DBG("PA: pix %04x bad pix / LU referenced from page %04x\n", + rpix, cur_pix); + // check for data page elsewhere + spiffs_page_ix data_pix; + res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, 0, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + if (data_pix == 0) { + // if not, allocate free page + spiffs_page_header new_ph; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); + new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = data_spix_offset + i; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix); + SPIFFS_CHECK_RES(res); + SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ %04x\n", data_pix); + } + // remap index + SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix %04x\n", cur_pix); + res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, data_pix, cur_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend - delete object\n", res); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); + // delete file + res = spiffs_page_delete(fs, cur_pix); + } else { + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + + } else if (rpix_within_range) { + + // valid reference + // read referenced page header + spiffs_page_header rp_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); + SPIFFS_CHECK_RES(res); + + // cross reference page header check + if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) || + rp_hdr.span_ix != data_spix_offset + i || + (rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) != + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) { + SPIFFS_CHECK_DBG("PA: pix %04x has inconsistent page header ix id/span:%04x/%04x, ref id/span:%04x/%04x flags:%02x\n", + rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i, + rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags); + // try finding correct page + spiffs_page_ix data_pix; + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, rpix, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + if (data_pix == 0) { + // not found, this index is badly borked + SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id %04x\n", p_hdr.obj_id); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + SPIFFS_CHECK_RES(res); + break; + } else { + // found it, so rewrite index + SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix %04x, rewrite ix pix %04x id %04x\n", + data_pix, cur_pix, p_hdr.obj_id); + res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + } else { + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + } + } + else { + // mark rpix as referenced + const u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits); + const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits; + if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) { + SPIFFS_CHECK_DBG("PA: pix %04x multiple referenced from page %04x\n", + rpix, cur_pix); + // Here, we should have fixed all broken references - getting this means there + // must be multiple files with same object id. Only solution is to delete + // the object which is referring to this page + SPIFFS_CHECK_DBG("PA: FIXUP: removing object %04x and page %04x\n", + p_hdr.obj_id, cur_pix); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + SPIFFS_CHECK_RES(res); + // extra precaution, delete this page also + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + restart = 1; + } + fs->work[rpix_byte_ix] |= (1<<(rpix_bit_ix + 1)); + } + } + } // for all index entries + } // found index + + // next page + cur_pix++; + } + // next block + cur_block++; + } + // check consistency bitmap + if (!restart) { + spiffs_page_ix objix_pix; + spiffs_page_ix rpix; + + int byte_ix; + int bit_ix; + for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) { + for (bit_ix = 0; !restart && bit_ix < 8/bits; bit_ix ++) { + u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7; + spiffs_page_ix cur_pix = pix_offset + byte_ix * (8/bits) + bit_ix; + + // 000 ok - free, unreferenced, not index + + if (bitmask == 0x1) { + + // 001 + SPIFFS_CHECK_DBG("PA: pix %04x USED, UNREFERENCED, not index\n", cur_pix); + + u8_t rewrite_ix_to_this = 0; + u8_t delete_page = 0; + // check corresponding object index entry + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix, + &rpix, &objix_pix); + if (res == SPIFFS_OK) { + if (((rpix == (spiffs_page_ix)-1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) { + // pointing to a bad page altogether, rewrite index to this + rewrite_ix_to_this = 1; + SPIFFS_CHECK_DBG("PA: corresponding ref is bad: %04x, rewrite to this %04x\n", rpix, cur_pix); + } else { + // pointing to something else, check what + spiffs_page_header rp_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr); + SPIFFS_CHECK_RES(res); + if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) && + ((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) == + (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) { + // pointing to something else valid, just delete this page then + SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: %04x, delete this %04x\n", rpix, cur_pix); + delete_page = 1; + } else { + // pointing to something weird, update index to point to this page instead + if (rpix != cur_pix) { + SPIFFS_CHECK_DBG("PA: corresponding ref is weird: %04x %s%s%s%s, rewrite this %04x\n", rpix, + (rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ", + (rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ", + (rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "", + (rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "", + cur_pix); + rewrite_ix_to_this = 1; + } else { + // should not happen, destined for fubar + } + } + } + } else if (res == SPIFFS_ERR_NOT_FOUND) { + SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete %04x\n", cur_pix); + delete_page = 1; + res = SPIFFS_OK; + } + + if (rewrite_ix_to_this) { + // if pointing to invalid page, redirect index to this page + SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id %04x data spix %04x to point to this pix: %04x\n", + p_hdr.obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + } else { + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + continue; + } else if (delete_page) { + SPIFFS_CHECK_DBG("PA: FIXUP: deleting page %04x\n", cur_pix); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + res = spiffs_page_delete(fs, cur_pix); + } + SPIFFS_CHECK_RES(res); + } + if (bitmask == 0x2) { + + // 010 + SPIFFS_CHECK_DBG("PA: pix %04x FREE, REFERENCED, not index\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + + // 011 ok - busy, referenced, not index + + if (bitmask == 0x4) { + + // 100 + SPIFFS_CHECK_DBG("PA: pix %04x FREE, unreferenced, INDEX\n", cur_pix); + + // this should never happen, major fubar + } + + // 101 ok - busy, unreferenced, index + + if (bitmask == 0x6) { + + // 110 + SPIFFS_CHECK_DBG("PA: pix %04x FREE, REFERENCED, INDEX\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + if (bitmask == 0x7) { + + // 111 + SPIFFS_CHECK_DBG("PA: pix %04x USED, REFERENCED, INDEX\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + } + } + } + // next page range + if (!restart) { + pix_offset += pages_per_scan; + } + } // while page range not reached end + return res; +} + +// Checks consistency amongst all pages and fixes irregularities +s32_t ICACHE_FLASH_ATTR spiffs_page_consistency_check(spiffs *fs) { + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0); + s32_t res = spiffs_page_consistency_check_i(fs); + if (res != SPIFFS_OK) { + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0); + } + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0); + return res; +} + +//--------------------------------------- +// Object index consistency + +// searches for given object id in temporary object id index, +// returns the index or -1 +static int ICACHE_FLASH_ATTR spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) { + int i; + spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; + obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) { + if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) { + return i; + } + } + return -1; +} + +s32_t ICACHE_FLASH_ATTR spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, + int cur_entry, u32_t user_data, void *user_p) { + s32_t res_c = SPIFFS_VIS_COUNTINUE; + s32_t res = SPIFFS_OK; + u32_t *log_ix = (u32_t *)user_p; + spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; + + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, + (cur_block * 256)/fs->block_count, 0); + + if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_page_header p_hdr; + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); + + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + + if (p_hdr.span_ix == 0 && + (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET)) { + SPIFFS_CHECK_DBG("IX: pix %04x, obj id:%04x spix:%04x header not fully deleted - deleting\n", + cur_pix, obj_id, p_hdr.span_ix); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + return res_c; + } + + if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + return res_c; + } + + if (p_hdr.span_ix == 0) { + // objix header page, register objid as reachable + int r = spiffs_object_index_search(fs, obj_id); + if (r == -1) { + // not registered, do it + obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + (*log_ix)++; + if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { + *log_ix = 0; + } + } + } else { // span index + // objix page, see if header can be found + int r = spiffs_object_index_search(fs, obj_id); + u8_t delete = 0; + if (r == -1) { + // not in temporary index, try finding it + spiffs_page_ix objix_hdr_pix; + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix); + res_c = SPIFFS_VIS_COUNTINUE_RELOAD; + if (res == SPIFFS_OK) { + // found, register as reachable + obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + } else if (res == SPIFFS_ERR_NOT_FOUND) { + // not found, register as unreachable + delete = 1; + obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG; + } else { + SPIFFS_CHECK_RES(res); + } + (*log_ix)++; + if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { + *log_ix = 0; + } + } else { + // in temporary index, check reachable flag + if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) { + // registered as unreachable + delete = 1; + } + } + + if (delete) { + SPIFFS_CHECK_DBG("IX: FIXUP: pix %04x, obj id:%04x spix:%04x is orphan index - deleting\n", + cur_pix, obj_id, p_hdr.span_ix); + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + } + } // span index + } // valid object index id + + return res_c; +} + +// Removes orphaned and partially deleted index pages. +// Scans for index pages. When an index page is found, corresponding index header is searched for. +// If no such page exists, the index page cannot be reached as no index header exists and must be +// deleted. +s32_t ICACHE_FLASH_ATTR spiffs_object_index_consistency_check(spiffs *fs) { + s32_t res = SPIFFS_OK; + // impl note: + // fs->work is used for a temporary object index memory, listing found object ids and + // indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit. + // In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate + // a reachable/unreachable object id. + c_memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + u32_t obj_id_log_ix = 0; + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix, + 0, 0); + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + if (res != SPIFFS_OK) { + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0); + } + if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0); + return res; +} + diff --git a/app/spiffs/spiffs_config.h b/app/spiffs/spiffs_config.h new file mode 100644 index 00000000..8a8fc4b3 --- /dev/null +++ b/app/spiffs/spiffs_config.h @@ -0,0 +1,210 @@ +/* + * spiffs_config.h + * + * Created on: Jul 3, 2013 + * Author: petera + */ + +#ifndef SPIFFS_CONFIG_H_ +#define SPIFFS_CONFIG_H_ + +// ----------- 8< ------------ +// Following includes are for the linux test build of spiffs +// These may/should/must be removed/altered/replaced in your target +// #include "params_test.h" +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_string.h" +#include "c_stddef.h" +#include "c_types.h" +// ----------- >8 ------------ + +typedef sint32_t s32_t; +typedef uint32_t u32_t; +typedef sint16_t s16_t; +typedef uint16_t u16_t; +typedef sint8_t s8_t; +typedef uint8_t u8_t; + +// compile time switches + +// Set generic spiffs debug output call. +#ifndef SPIFFS_DGB +#define SPIFFS_DBG(...) +#endif +// Set spiffs debug output call for garbage collecting. +#ifndef SPIFFS_GC_DGB +#define SPIFFS_GC_DBG(...) +#endif +// Set spiffs debug output call for caching. +#ifndef SPIFFS_CACHE_DGB +#define SPIFFS_CACHE_DBG(...) +#endif +// Set spiffs debug output call for system consistency checks. +#ifndef SPIFFS_CHECK_DGB +#define SPIFFS_CHECK_DBG(...) +#endif + +// Enable/disable API functions to determine exact number of bytes +// for filedescriptor and cache buffers. Once decided for a configuration, +// this can be disabled to reduce flash. +#ifndef SPIFFS_BUFFER_HELP +#define SPIFFS_BUFFER_HELP 0 +#endif + +// Enables/disable memory read caching of nucleus file system operations. +// If enabled, memory area must be provided for cache in SPIFFS_mount. +#ifndef SPIFFS_CACHE +#define SPIFFS_CACHE 1 +#endif +#if SPIFFS_CACHE +// Enables memory write caching for file descriptors in hydrogen +#ifndef SPIFFS_CACHE_WR +#define SPIFFS_CACHE_WR 1 +#endif + +// Enable/disable statistics on caching. Debug/test purpose only. +#ifndef SPIFFS_CACHE_STATS +#define SPIFFS_CACHE_STATS 0 +#endif +#endif + +// Always check header of each accessed page to ensure consistent state. +// If enabled it will increase number of reads, will increase flash. +#ifndef SPIFFS_PAGE_CHECK +#define SPIFFS_PAGE_CHECK 1 +#endif + +// Define maximum number of gc runs to perform to reach desired free pages. +#ifndef SPIFFS_GC_MAX_RUNS +#define SPIFFS_GC_MAX_RUNS 3 +#endif + +// Enable/disable statistics on gc. Debug/test purpose only. +#ifndef SPIFFS_GC_STATS +#define SPIFFS_GC_STATS 0 +#endif + +// Garbage collecting examines all pages in a block which and sums up +// to a block score. Deleted pages normally gives positive score and +// used pages normally gives a negative score (as these must be moved). +// To have a fair wear-leveling, the erase age is also included in score, +// whose factor normally is the most positive. +// The larger the score, the more likely it is that the block will +// picked for garbage collection. + +// Garbage collecting heuristics - weight used for deleted pages. +#ifndef SPIFFS_GC_HEUR_W_DELET +#define SPIFFS_GC_HEUR_W_DELET (5) +#endif +// Garbage collecting heuristics - weight used for used pages. +#ifndef SPIFFS_GC_HEUR_W_USED +#define SPIFFS_GC_HEUR_W_USED (-1) +#endif +// Garbage collecting heuristics - weight used for time between +// last erased and erase of this block. +#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE +#define SPIFFS_GC_HEUR_W_ERASE_AGE (50) +#endif + +// Object name maximum length. +#ifndef SPIFFS_OBJ_NAME_LEN +#define SPIFFS_OBJ_NAME_LEN (32) +#endif + +// Size of buffer allocated on stack used when copying data. +// Lower value generates more read/writes. No meaning having it bigger +// than logical page size. +#ifndef SPIFFS_COPY_BUFFER_STACK +#define SPIFFS_COPY_BUFFER_STACK (64) +#endif + +// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level +// These should be defined on a multithreaded system + +// define this to entering a mutex if you're running on a multithreaded system +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) +#endif +// define this to exiting a mutex if you're running on a multithreaded system +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) +#endif + + +// Enable if only one spiffs instance with constant configuration will exist +// on the target. This will reduce calculations, flash and memory accesses. +// Parts of configuration must be defined below instead of at time of mount. +#ifndef SPIFFS_SINGLETON +#define SPIFFS_SINGLETON 0 +#endif + +#if SPIFFS_SINGLETON +// Instead of giving parameters in config struct, singleton build must +// give parameters in defines below. +#ifndef SPIFFS_CFG_PHYS_SZ +#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*1024*2) +#endif +#ifndef SPIFFS_CFG_PHYS_ERASE_SZ +#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (65536) +#endif +#ifndef SPIFFS_CFG_PHYS_ADDR +#define SPIFFS_CFG_PHYS_ADDR(ignore) (0) +#endif +#ifndef SPIFFS_CFG_LOG_PAGE_SZ +#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) +#endif +#ifndef SPIFFS_CFG_LOG_BLOCK_SZ +#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (65536) +#endif +#endif + +// Set SPFIFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function +// in the api. This function will visualize all filesystem using given printf +// function. +#ifndef SPIFFS_TEST_VISUALISATION +#define SPIFFS_TEST_VISUALISATION 1 +#endif +#if SPIFFS_TEST_VISUALISATION +#ifndef spiffs_printf +#define spiffs_printf(...) c_printf(__VA_ARGS__) +#endif +// spiffs_printf argument for a free page +#ifndef SPIFFS_TEST_VIS_FREE_STR +#define SPIFFS_TEST_VIS_FREE_STR "_" +#endif +// spiffs_printf argument for a deleted page +#ifndef SPIFFS_TEST_VIS_DELE_STR +#define SPIFFS_TEST_VIS_DELE_STR "/" +#endif +// spiffs_printf argument for an index page for given object id +#ifndef SPIFFS_TEST_VIS_INDX_STR +#define SPIFFS_TEST_VIS_INDX_STR(id) "i" +#endif +// spiffs_printf argument for a data page for given object id +#ifndef SPIFFS_TEST_VIS_DATA_STR +#define SPIFFS_TEST_VIS_DATA_STR(id) "d" +#endif +#endif + +// Types depending on configuration such as the amount of flash bytes +// given to spiffs file system in total (spiffs_file_system_size), +// the logical block size (log_block_size), and the logical page size +// (log_page_size) + +// Block index type. Make sure the size of this type can hold +// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size +typedef u16_t spiffs_block_ix; +// Page index type. Make sure the size of this type can hold +// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size +typedef u16_t spiffs_page_ix; +// Object id type - most significant bit is reserved for index flag. Make sure the +// size of this type can hold the highest object id on a full system, +// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2 +typedef u16_t spiffs_obj_id; +// Object span index type. Make sure the size of this type can +// hold the largest possible span index on the system - +// i.e. (spiffs_file_system_size / log_page_size) - 1 +typedef u16_t spiffs_span_ix; + +#endif /* SPIFFS_CONFIG_H_ */ diff --git a/app/spiffs/spiffs_gc.c b/app/spiffs/spiffs_gc.c new file mode 100644 index 00000000..e7ec1d15 --- /dev/null +++ b/app/spiffs/spiffs_gc.c @@ -0,0 +1,550 @@ +#include "spiffs.h" +#include "spiffs_nucleus.h" + +// Erases a logical block and updates the erase counter. +// If cache is enabled, all pages that might be cached in this block +// is dropped. +static s32_t ICACHE_FLASH_ATTR spiffs_gc_erase_block( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res; + u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix); + s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + SPIFFS_GC_DBG("gc: erase block %i\n", bix); + + // here we ignore res, just try erasing the block + while (size > 0) { + SPIFFS_GC_DBG("gc: erase %08x:%08x\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + (void)fs->cfg.hal_erase_f(addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs); + size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs); + } + fs->free_blocks++; + + // register erase count for this block + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count); + SPIFFS_CHECK_RES(res); + + fs->max_erase_count++; + if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) { + fs->max_erase_count = 0; + } + +#if SPIFFS_CACHE + { + int i; + for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) { + spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i); + } + } +#endif + return res; +} + +// Searches for blocks where all entries are deleted - if one is found, +// the block is erased. Compared to the non-quick gc, the quick one ensures +// that no updates are needed on existing objects on pages that are erased. +s32_t ICACHE_FLASH_ATTR spiffs_gc_quick( + spiffs *fs) { + s32_t res = SPIFFS_OK; + u32_t blocks = fs->block_count; + spiffs_block_ix cur_block = 0; + u32_t cur_block_addr = 0; + int cur_entry = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + + SPIFFS_GC_DBG("gc_quick: running\n", cur_block); +#if SPIFFS_GC_STATS + fs->stats_gc_runs++; +#endif + + u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // find fully deleted blocks + // check each block + while (res == SPIFFS_OK && blocks--) { + u16_t deleted_pages_in_block = 0; + + int obj_lookup_page = 0; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_DELETED) { + deleted_pages_in_block++; + } else if (obj_id == SPIFFS_OBJ_ID_FREE) { + // kill scan, go for next block + obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); + break; + } else { + // kill scan, go for next block + obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); + break; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + + if (res == SPIFFS_OK && deleted_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // found a fully deleted block + fs->stats_p_deleted -= deleted_pages_in_block; + res = spiffs_gc_erase_block(fs, cur_block); + return res; + } + + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block + + return res; +} + +// Checks if garbaga collecting is necessary. If so a candidate block is found, +// cleansed and erased +s32_t ICACHE_FLASH_ATTR spiffs_gc_check( + spiffs *fs, + u32_t len) { + s32_t res; + u32_t free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count + - fs->stats_p_allocated - fs->stats_p_deleted; + int tries = 0; + + if (fs->free_blocks > 3 && + len < free_pages * SPIFFS_DATA_PAGE_SIZE(fs)) { + return SPIFFS_OK; + } + + //printf("gcing started %i dirty, blocks %i free, want %i bytes\n", fs->stats_p_allocated + fs->stats_p_deleted, fs->free_blocks, len); + + do { + SPIFFS_GC_DBG("\ngc_check #%i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n", + tries, + fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted), + len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs)); + + spiffs_block_ix *cands; + int count; + spiffs_block_ix cand; + res = spiffs_gc_find_candidate(fs, &cands, &count); + SPIFFS_CHECK_RES(res); + if (count == 0) { + SPIFFS_GC_DBG("gc_check: no candidates, return\n"); + return res; + } +#if SPIFFS_GC_STATS + fs->stats_gc_runs++; +#endif + cand = cands[0]; + fs->cleaning = 1; + //printf("gcing: cleaning block %i\n", cand); + res = spiffs_gc_clean(fs, cand); + fs->cleaning = 0; + if (res < 0) { + SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res); + } else { + SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res); + } + SPIFFS_CHECK_RES(res); + + res = spiffs_gc_erase_page_stats(fs, cand); + SPIFFS_CHECK_RES(res); + + res = spiffs_gc_erase_block(fs, cand); + SPIFFS_CHECK_RES(res); + + free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count + - fs->stats_p_allocated - fs->stats_p_deleted; + + } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || + len > free_pages*SPIFFS_DATA_PAGE_SIZE(fs))); + SPIFFS_GC_DBG("gc_check: finished\n"); + + //printf("gcing finished %i dirty, blocks %i free, %i pages free, %i tries, res %i\n", + // fs->stats_p_allocated + fs->stats_p_deleted, + // fs->free_blocks, free_pages, tries, res); + + return res; +} + +// Updates page statistics for a block that is about to be erased +s32_t ICACHE_FLASH_ATTR spiffs_gc_erase_page_stats( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res = SPIFFS_OK; + int obj_lookup_page = 0; + u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = 0; + u32_t dele = 0; + u32_t allo = 0; + + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + dele++; + } else { + allo++; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + SPIFFS_GC_DBG("gc_check: wipe pallo:%i pdele:%i\n", allo, dele); + fs->stats_p_allocated -= allo; + fs->stats_p_deleted -= dele; + return res; +} + +// Finds block candidates to erase +s32_t ICACHE_FLASH_ATTR spiffs_gc_find_candidate( + spiffs *fs, + spiffs_block_ix **block_candidates, + int *candidate_count) { + s32_t res = SPIFFS_OK; + u32_t blocks = fs->block_count; + spiffs_block_ix cur_block = 0; + u32_t cur_block_addr = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = 0; + + // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score + int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t))); + *candidate_count = 0; + c_memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + + // divide up work area into block indices and scores + // todo alignment? + spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work; + s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix)); + + *block_candidates = cand_blocks; + + u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // check each block + while (res == SPIFFS_OK && blocks--) { + u16_t deleted_pages_in_block = 0; + u16_t used_pages_in_block = 0; + + int obj_lookup_page = 0; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + // when a free entry is encountered, scan logic ensures that all following entries are free also + break; + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + deleted_pages_in_block++; + } else { + used_pages_in_block++; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + + // calculate score and insert into candidate table + // stoneage sort, but probably not so many blocks + if (res == SPIFFS_OK && deleted_pages_in_block > 0) { + // read erase count + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, cur_block), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + spiffs_obj_id erase_age; + if (fs->max_erase_count > erase_count) { + erase_age = fs->max_erase_count - erase_count; + } else { + erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count); + } + + s32_t score = + deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + + used_pages_in_block * SPIFFS_GC_HEUR_W_USED + + erase_age * SPIFFS_GC_HEUR_W_ERASE_AGE; + int cand_ix = 0; + SPIFFS_GC_DBG("gc_check: bix:%i del:%i use:%i score:%i\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); + while (cand_ix < max_candidates) { + if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) { + cand_blocks[cand_ix] = cur_block; + cand_scores[cand_ix] = score; + break; + } else if (cand_scores[cand_ix] < score) { + int reorder_cand_ix = max_candidates - 2; + while (reorder_cand_ix >= cand_ix) { + cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix]; + cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix]; + reorder_cand_ix--; + } + cand_blocks[cand_ix] = cur_block; + cand_scores[cand_ix] = score; + break; + } + cand_ix++; + } + (*candidate_count)++; + } + + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block + + return res; +} + +typedef enum { + FIND_OBJ_DATA, + MOVE_OBJ_DATA, + MOVE_OBJ_IX, + FINISHED +} spiffs_gc_clean_state; + +typedef struct { + spiffs_gc_clean_state state; + spiffs_obj_id cur_obj_id; + spiffs_span_ix cur_objix_spix; + spiffs_page_ix cur_objix_pix; + int stored_scan_entry_index; + u8_t obj_id_found; +} spiffs_gc; + +// Empties given block by moving all data into free pages of another block +// Strategy: +// loop: +// scan object lookup for object data pages +// for first found id, check spix and load corresponding object index page to memory +// push object scan lookup entry index +// rescan object lookup, find data pages with same id and referenced by same object index +// move data page, update object index in memory +// when reached end of lookup, store updated object index +// pop object scan lookup entry index +// repeat loop until end of object lookup +// scan object lookup again for remaining object index pages, move to new page in other block +// +s32_t ICACHE_FLASH_ATTR spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { + s32_t res = SPIFFS_OK; + u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + int cur_entry = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_gc gc; + spiffs_page_ix cur_pix = 0; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + + SPIFFS_GC_DBG("gc_clean: cleaning block %i\n", bix); + + c_memset(&gc, 0, sizeof(spiffs_gc)); + gc.state = FIND_OBJ_DATA; + + if (fs->free_cursor_block_ix == bix) { + // move free cursor to next block, cannot use free pages from the block we want to clean + fs->free_cursor_block_ix = (bix+1)%fs->block_count; + fs->free_cursor_obj_lu_entry = 0; + SPIFFS_GC_DBG("gc_clean: move free cursor to block %i\n", fs->free_cursor_block_ix); + } + + while (res == SPIFFS_OK && gc.state != FINISHED) { + SPIFFS_GC_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry); + gc.obj_id_found = 0; + + // scan through lookup pages + int obj_lookup_page = cur_entry / entries_per_page; + u8_t scan = 1; + // check each object lookup page + while (scan && res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (scan && res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry); + + // act upon object id depending on gc state + switch (gc.state) { + case FIND_OBJ_DATA: + if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && + ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) { + SPIFFS_GC_DBG("gc_clean: FIND_DATA state:%i - found obj id %04x\n", gc.state, obj_id); + gc.obj_id_found = 1; + gc.cur_obj_id = obj_id; + scan = 0; + } + break; + case MOVE_OBJ_DATA: + if (obj_id == gc.cur_obj_id) { + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page %04x:%04x @ %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); + if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) { + SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n"); + } else { + spiffs_page_ix new_data_pix; + if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { + // move page + res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix %04x:%04x page %04x to %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix); + SPIFFS_CHECK_RES(res); + // move wipes obj_lu, reload it + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } else { + // page is deleted but not deleted in lookup, scrap it + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + new_data_pix = SPIFFS_OBJ_ID_FREE; + } + // update memory representation of object index page with new data page + if (gc.cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix; + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix; + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + } + } + } + break; + case MOVE_OBJ_IX: + if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && + (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { + // found an index object id + spiffs_page_header p_hdr; + spiffs_page_ix new_pix; + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { + // move page + res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix %04x:%04x page %04x to %04x\n", obj_id, p_hdr.span_ix, cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, obj_id, p_hdr.span_ix, new_pix, 0); + // move wipes obj_lu, reload it + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } else { + // page is deleted but not deleted in lookup, scrap it + SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_page_delete(fs, cur_pix); + if (res == SPIFFS_OK) { + spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0); + } + } + SPIFFS_CHECK_RES(res); + } + break; + default: + scan = 0; + break; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + + if (res != SPIFFS_OK) break; + + // state finalization and switch + switch (gc.state) { + case FIND_OBJ_DATA: + if (gc.obj_id_found) { + // find out corresponding obj ix page and load it to memory + spiffs_page_header p_hdr; + spiffs_page_ix objix_pix; + gc.stored_scan_entry_index = cur_entry; + cur_entry = 0; + gc.state = MOVE_OBJ_DATA; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); + SPIFFS_CHECK_RES(res); + gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix); + SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:%04x\n", gc.cur_objix_spix); + res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page %04x\n", objix_pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix); + gc.cur_objix_pix = objix_pix; + } else { + gc.state = MOVE_OBJ_IX; + cur_entry = 0; // restart entry scan index + } + break; + case MOVE_OBJ_DATA: { + // store modified objix (hdr) page + spiffs_page_ix new_objix_pix; + gc.state = FIND_OBJ_DATA; + cur_entry = gc.stored_scan_entry_index; + if (gc.cur_objix_spix == 0) { + // store object index header page + res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, &new_objix_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, %04x:%04x\n", new_objix_pix, 0); + SPIFFS_CHECK_RES(res); + } else { + // store object index page + spiffs_page_ix new_objix_pix; + res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, %04x:%04x\n", new_objix_pix, objix->p_hdr.span_ix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + break; + case MOVE_OBJ_IX: + gc.state = FINISHED; + break; + default: + cur_entry = 0; + break; + } + SPIFFS_GC_DBG("gc_clean: state-> %i\n", gc.state); + } // while state != FINISHED + + + return res; +} + diff --git a/app/spiffs/spiffs_hydrogen.c b/app/spiffs/spiffs_hydrogen.c new file mode 100644 index 00000000..a4597c6a --- /dev/null +++ b/app/spiffs/spiffs_hydrogen.c @@ -0,0 +1,790 @@ +/* + * spiffs_hydrogen.c + * + * Created on: Jun 16, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh); + +#if SPIFFS_BUFFER_HELP +u32_t ICACHE_FLASH_ATTR SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) { + return num_descs * sizeof(spiffs_fd); +} +#if SPIFFS_CACHE +u32_t ICACHE_FLASH_ATTR SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) { + return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); +} +#endif +#endif + +s32_t ICACHE_FLASH_ATTR SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size, + spiffs_check_callback check_cb_f) { + SPIFFS_LOCK(fs); + c_memset(fs, 0, sizeof(spiffs)); + c_memcpy(&fs->cfg, config, sizeof(spiffs_config)); + fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); + fs->work = &work[0]; + fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; + c_memset(fd_space, 0, fd_space_size); + // align fd_space pointer to pointer size byte boundary, below is safe + u8_t ptr_size = sizeof(void*); +// #pragma GCC diagnostic push +// #pragma GCC diagnostic ignored "-Wpointer-to-int-cast" + u8_t addr_lsb = (u8_t)(((u32_t)fd_space) & (ptr_size-1)); +// #pragma GCC diagnostic pop + if (addr_lsb) { + fd_space += (ptr_size-addr_lsb); + fd_space_size -= (ptr_size-addr_lsb); + } + fs->fd_space = fd_space; + fs->fd_count = (fd_space_size/sizeof(spiffs_fd)); + + // align cache pointer to 4 byte boundary, below is safe +// #pragma GCC diagnostic push +// #pragma GCC diagnostic ignored "-Wpointer-to-int-cast" + addr_lsb = (u8_t)(((u32_t)cache) & (ptr_size-1)); +// #pragma GCC diagnostic pop + if (addr_lsb) { + cache = (u8_t *)cache + (ptr_size-addr_lsb); + cache_size -= (ptr_size-addr_lsb); + } + if (cache_size & (ptr_size-1)) { + cache_size -= (cache_size & (ptr_size-1)); + } +#if SPIFFS_CACHE + fs->cache = cache; + fs->cache_size = cache_size; + spiffs_cache_init(fs); +#endif + + s32_t res = spiffs_obj_lu_scan(fs); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_DBG("page index byte len: %i\n", SPIFFS_CFG_LOG_PAGE_SZ(fs)); + SPIFFS_DBG("object lookup pages: %i\n", SPIFFS_OBJ_LOOKUP_PAGES(fs)); + SPIFFS_DBG("page pages per block: %i\n", SPIFFS_PAGES_PER_BLOCK(fs)); + SPIFFS_DBG("page header length: %i\n", sizeof(spiffs_page_header)); + SPIFFS_DBG("object header index entries: %i\n", SPIFFS_OBJ_HDR_IX_LEN(fs)); + SPIFFS_DBG("object index entries: %i\n", SPIFFS_OBJ_IX_LEN(fs)); + SPIFFS_DBG("available file descriptors: %i\n", fs->fd_count); + SPIFFS_DBG("free blocks: %i\n", fs->free_blocks); + + fs->check_cb_f = check_cb_f; + + SPIFFS_UNLOCK(fs); + + return 0; +} + +void ICACHE_FLASH_ATTR SPIFFS_unmount(spiffs *fs) { + if (!SPIFFS_CHECK_MOUNT(fs)) return; + SPIFFS_LOCK(fs); + int i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr != 0) { +#if SPIFFS_CACHE + (void)spiffs_fflush_cache(fs, cur_fd->file_nbr); +#endif + spiffs_fd_return(fs, cur_fd->file_nbr); + } + } + fs->block_count = 0; + SPIFFS_UNLOCK(fs); +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_errno(spiffs *fs) { + return fs->errno; +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) { + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + spiffs_obj_id obj_id; + s32_t res; + + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (u8_t*)path, SPIFFS_TYPE_FILE, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +} + +spiffs_file ICACHE_FLASH_ATTR SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + + s32_t res = spiffs_fd_find_new(fs, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)path, &pix); + if ((flags & SPIFFS_CREAT) == 0) { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((flags & SPIFFS_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { + spiffs_obj_id obj_id; + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (u8_t*)path, SPIFFS_TYPE_FILE, &pix); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + flags &= ~SPIFFS_TRUNC; + } else { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + res = spiffs_object_open_by_page(fs, pix, fd, flags, flags); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + if (flags & SPIFFS_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return fd->file_nbr; +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, u32_t len) { + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_RDONLY) == 0) { + res = SPIFFS_ERR_NOT_READABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + if (fd->fdoffset + len >= fd->size) { + // reading beyond file size + s32_t avail = fd->size - fd->fdoffset; + if (avail <= 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT); + } + res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t*)buf); + if (res == SPIFFS_OK) { + fd->fdoffset += avail; + SPIFFS_UNLOCK(fs); + return avail; + } else { + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + } else { + // reading within file size + res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t*)buf); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return len; +} + +static s32_t ICACHE_FLASH_ATTR spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) { + s32_t res = SPIFFS_OK; + s32_t remaining = len; + if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) { + s32_t m_len = MIN(fd->size - offset, len); + res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len); + SPIFFS_CHECK_RES(res); + remaining -= m_len; + buf = (u8_t *)buf + m_len; + offset += m_len; + } + if (remaining > 0) { + res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining); + SPIFFS_CHECK_RES(res); + } + return len; + +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) { + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + u32_t offset; + + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + offset = fd->fdoffset; + +#if SPIFFS_CACHE_WR + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } +#endif + if (fd->flags & SPIFFS_APPEND) { + if (fd->size == SPIFFS_UNDEFINED_LEN) { + offset = 0; + } else { + offset = fd->size; + } +#if SPIFFS_CACHE_WR + if (fd->cache_page) { + offset = MAX(offset, fd->cache_page->offset + fd->cache_page->size); + } +#endif + } + + SPIFFS_DBG("SPIFFS_write %i %04x offs:%i len %i\n", fh, fd->obj_id, offset, len); + +#if SPIFFS_CACHE_WR + if ((fd->flags & SPIFFS_DIRECT) == 0) { + if (len < SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + // small write, try to cache it + u8_t alloc_cpage = 1; + if (fd->cache_page) { + // have a cached page for this fd already, check cache page boundaries + if (offset < fd->cache_page->offset || // writing before cache + offset > fd->cache_page->offset + fd->cache_page->size || // writing after cache + offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page + { + // boundary violation, write back cache first and allocate new + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:&04x, boundary viol, offs:%i size:%i\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fd_release(fs, fd->cache_page); + } else { + // writing within cache + alloc_cpage = 0; + } + } + + if (alloc_cpage) { + fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd); + if (fd->cache_page) { + fd->cache_page->offset = offset; + fd->cache_page->size = 0; + SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page %i for fd %i:%04x\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id); + } + } + + if (fd->cache_page) { + u32_t offset_in_cpage = offset - fd->cache_page->offset; + SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page %i for fd %i:%04x, offs %i:%i len %i\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, + offset, offset_in_cpage, len); + spiffs_cache *cache = spiffs_get_cache(fs); + u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix); + c_memcpy(&cpage_data[offset_in_cpage], buf, len); + fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return len; + } else { + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES(fs, res); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return res; + } + } else { + // big write, no need to cache it - but first check if there is a cached write already + if (fd->cache_page) { + // write back cache first + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, big write, offs:%i size:%i\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fd_release(fs, fd->cache_page); + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES(fs, res); + } + } + } +#endif + + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES(fs, res); + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + switch (whence) { + case SPIFFS_SEEK_CUR: + offs = fd->fdoffset+offs; + break; + case SPIFFS_SEEK_END: + offs = (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size) + offs; + break; + } + + if (offs > fd->size) { + res = SPIFFS_ERR_END_OF_OBJECT; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + spiffs_span_ix data_spix = offs / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (fd->cursor_objix_spix != objix_spix) { + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span( + fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->cursor_objix_spix = objix_spix; + fd->cursor_objix_pix = pix; + } + fd->fdoffset = offs; + + SPIFFS_UNLOCK(fs); + + return 0; +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_remove(spiffs *fs, const char *path) { + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + s32_t res; + + res = spiffs_fd_find_new(fs, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)path, &pix); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0,0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_truncate(fd, 0, 1); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return 0; +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_fremove(spiffs *fs, spiffs_file fh) { + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + +#if SPIFFS_CACHE_WR + spiffs_cache_fd_release(fs, fd->cache_page); +#endif + + res = spiffs_object_truncate(fd, 0, 1); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return 0; +} + +static s32_t ICACHE_FLASH_ATTR spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) { + spiffs_page_object_ix_header objix_hdr; + spiffs_obj_id obj_id; + s32_t res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_API_CHECK_RES(fs, res); + + u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , pix)) + + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_obj_id); + res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, fh, + obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id); + SPIFFS_API_CHECK_RES(fs, res); + + s->obj_id = obj_id; + s->type = objix_hdr.type; + s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN); + + return res; +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) { + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + s32_t res; + spiffs_page_ix pix; + + res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)path, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_stat_pix(fs, pix, 0, s); + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + res = spiffs_stat_pix(fs, fd->objix_hdr_pix, fh, s); + + SPIFFS_UNLOCK(fs); + + return res; +} + +// Checks if there are any cached writes for the object id associated with +// given filehandle. If so, these writes are flushed. +static s32_t ICACHE_FLASH_ATTR spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { + s32_t res = SPIFFS_OK; +#if SPIFFS_CACHE_WR + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); + + if ((fd->flags & SPIFFS_DIRECT) == 0) { + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } + if (fd->cache_page) { + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, flush, offs:%i size:%i\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + if (res < SPIFFS_OK) { + fs->errno = res; + } + spiffs_cache_fd_release(fs, fd->cache_page); + } + } +#endif + + return res; +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_fflush(spiffs *fs, spiffs_file fh) { + SPIFFS_API_CHECK_MOUNT(fs); + s32_t res = SPIFFS_OK; +#if SPIFFS_CACHE_WR + SPIFFS_LOCK(fs); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs,res); + SPIFFS_UNLOCK(fs); +#endif + + return res; +} + +void ICACHE_FLASH_ATTR SPIFFS_close(spiffs *fs, spiffs_file fh) { + if (!SPIFFS_CHECK_MOUNT(fs)) { + fs->errno = SPIFFS_ERR_NOT_MOUNTED; + return; + } + SPIFFS_LOCK(fs); + +#if SPIFFS_CACHE + spiffs_fflush_cache(fs, fh); +#endif + spiffs_fd_return(fs, fh); + + SPIFFS_UNLOCK(fs); +} + +spiffs_DIR *ICACHE_FLASH_ATTR SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) { + if (!SPIFFS_CHECK_MOUNT(fs)) { + fs->errno = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + d->fs = fs; + d->block = 0; + d->entry = 0; + return d; +} + +static s32_t ICACHE_FLASH_ATTR spiffs_read_dir_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + u32_t user_data, + void *user_p) { + s32_t res; + spiffs_page_object_ix_header objix_hdr; + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } + + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + if (res != SPIFFS_OK) return res; + if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && + objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags& (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + struct spiffs_dirent *e = (struct spiffs_dirent *)user_p; + e->obj_id = obj_id; + strcpy((char *)e->name, (char *)objix_hdr.name); + e->type = objix_hdr.type; + e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + return SPIFFS_OK; + } + + return SPIFFS_VIS_COUNTINUE; +} + +struct spiffs_dirent *ICACHE_FLASH_ATTR SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { + if (!SPIFFS_CHECK_MOUNT(d->fs)) { + d->fs->errno = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + SPIFFS_LOCK(fs); + + spiffs_block_ix bix; + int entry; + s32_t res; + struct spiffs_dirent *ret = 0; + + res = spiffs_obj_lu_find_entry_visitor(d->fs, + d->block, + d->entry, + SPIFFS_VIS_NO_WRAP, + 0, + spiffs_read_dir_v, + 0, + e, + &bix, + &entry); + if (res == SPIFFS_OK) { + d->block = bix; + d->entry = entry + 1; + ret = e; + } else { + d->fs->errno = res; + } + SPIFFS_UNLOCK(fs); + return ret; +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_closedir(spiffs_DIR *d) { + SPIFFS_API_CHECK_MOUNT(d->fs); + return 0; +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_check(spiffs *fs) { + s32_t res; + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_lookup_consistency_check(fs, 0); + + res = spiffs_object_index_consistency_check(fs); +// NODE_ERR("before spiffs_object_index_consistency_check\n"); + res = spiffs_page_consistency_check(fs); +// NODE_ERR("spiffs_page_consistency_check\n"); + res = spiffs_obj_lu_scan(fs); +// NODE_ERR("spiffs_obj_lu_scan\n"); + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_eof(spiffs *fs, spiffs_file fh) { + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + res = (fd->fdoffset == fd->size); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t ICACHE_FLASH_ATTR SPIFFS_tell(spiffs *fs, spiffs_file fh) { + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + res = fd->fdoffset; + + SPIFFS_UNLOCK(fs); + return res; +} + +#if SPIFFS_TEST_VISUALISATION +s32_t ICACHE_FLASH_ATTR SPIFFS_vis(spiffs *fs) { + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_block_ix bix = 0; + + while (bix < fs->block_count) { + // check each object lookup page + int obj_lookup_page = 0; + int cur_entry = 0; + + while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; + if (cur_entry == 0) { + spiffs_printf("%4i ", bix); + } else if ((cur_entry & 0x3f) == 0) { + spiffs_printf(" "); + } + if (obj_id == SPIFFS_OBJ_ID_FREE) { + spiffs_printf(SPIFFS_TEST_VIS_FREE_STR); + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + spiffs_printf(SPIFFS_TEST_VIS_DELE_STR); + } else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG){ + spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id)); + } else { + spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id)); + } + cur_entry++; + if ((cur_entry & 0x3f) == 0) { + spiffs_printf("\n"); + } + } // per entry + obj_lookup_page++; + } // per object lookup page + + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + if (erase_count != (spiffs_obj_id)-1) { + spiffs_printf("\tera_cnt: %i\n", erase_count); + } else { + spiffs_printf("\tera_cnt: N/A\n"); + } + + bix++; + } // per block + + spiffs_printf("era_cnt_max: %i\n", fs->max_erase_count); + spiffs_printf("last_errno: %i\n", fs->errno); + spiffs_printf("blocks: %i\n", fs->block_count); + spiffs_printf("free_blocks: %i\n", fs->free_blocks); + spiffs_printf("page_alloc: %i\n", fs->stats_p_allocated); + spiffs_printf("page_delet: %i\n", fs->stats_p_deleted); + + SPIFFS_UNLOCK(fs); + return res; +} +#endif diff --git a/app/spiffs/spiffs_nucleus.c b/app/spiffs/spiffs_nucleus.c new file mode 100644 index 00000000..01b5f94e --- /dev/null +++ b/app/spiffs/spiffs_nucleus.c @@ -0,0 +1,1760 @@ +#include "spiffs.h" +#include "spiffs_nucleus.h" + +static s32_t ICACHE_FLASH_ATTR spiffs_page_data_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix)-1) { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_REF_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_REF_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) { + // referring to a bad page + return SPIFFS_ERR_INDEX_REF_INVALID; + } +#if SPIFFS_PAGE_CHECK + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_DATA(ph, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, spix); +#endif + return res; +} + +static s32_t ICACHE_FLASH_ATTR spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix)-1) { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) { + // referring to a bad page + return SPIFFS_ERR_INDEX_INVALID; + } +#if SPIFFS_PAGE_CHECK + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix); +#endif + return res; +} + +#if !SPIFFS_CACHE + +s32_t ICACHE_FLASH_ATTR spiffs_phys_rd( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *dst) { + return fs->cfg.hal_read_f(addr, len, dst); +} + +s32_t ICACHE_FLASH_ATTR spiffs_phys_wr( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *src) { + return fs->cfg.hal_write_f(addr, len, src); +} + +#endif + +s32_t ICACHE_FLASH_ATTR spiffs_phys_cpy( + spiffs *fs, + spiffs_file fh, + u32_t dst, + u32_t src, + u32_t len) { + s32_t res; + u8_t b[SPIFFS_COPY_BUFFER_STACK]; + while (len > 0) { + u32_t chunk_size = MIN(SPIFFS_COPY_BUFFER_STACK, len); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS, fh, src, chunk_size, b); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD, fh, dst, chunk_size, b); + SPIFFS_CHECK_RES(res); + len -= chunk_size; + src += chunk_size; + dst += chunk_size; + } + return SPIFFS_OK; +} + +// Find object lookup entry containing given id with visitor. +// Iterate over object lookup pages in each block until a given object id entry is found. +// When found, the visitor function is called with block index, entry index and user_data. +// If visitor returns SPIFFS_VIS_CONTINUE, the search goes on. Otherwise, the search will be +// ended and visitor's return code is returned to caller. +// If no visitor is given (0) the search returns on first entry with matching object id. +// If no match is found in all look up, SPIFFS_VIS_END is returned. +// @param fs the file system +// @param starting_block the starting block to start search in +// @param starting_lu_entry the look up index entry to start search in +// @param flags ored combination of SPIFFS_VIS_CHECK_ID, SPIFFS_VIS_CHECK_PH, +// SPIFFS_VIS_NO_WRAP +// @param obj_id argument object id +// @param v visitor callback function +// @param user_data any data, passed to the callback visitor function +// @param user_p any pointer, passed to the callback visitor function +// @param block_ix reported block index where match was found +// @param lu_entry reported look up index where match was found +s32_t ICACHE_FLASH_ATTR spiffs_obj_lu_find_entry_visitor( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + u8_t flags, + spiffs_obj_id obj_id, + spiffs_visitor_f v, + u32_t user_data, + void *user_p, + spiffs_block_ix *block_ix, + int *lu_entry) { + s32_t res = SPIFFS_OK; + s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs); + spiffs_block_ix cur_block = starting_block; + u32_t cur_block_addr = starting_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = starting_lu_entry; + u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // wrap initial + if (cur_entry >= SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { + cur_entry = 0; + cur_block++; + cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } + } + + // check each block + while (res == SPIFFS_OK && entry_count > 0) { + int obj_lookup_page = cur_entry / entries_per_page; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && // for non-last obj lookup pages + cur_entry < SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) // for last obj lookup page + { + if ((flags & SPIFFS_VIS_CHECK_ID) == 0 || obj_lu_buf[cur_entry-entry_offset] == obj_id) { + if (block_ix) *block_ix = cur_block; + if (lu_entry) *lu_entry = cur_entry; + if (v) { + res = v( + fs, + (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry-entry_offset], + cur_block, + cur_entry, + user_data, + user_p); + if (res == SPIFFS_VIS_COUNTINUE || res == SPIFFS_VIS_COUNTINUE_RELOAD) { + if (res == SPIFFS_VIS_COUNTINUE_RELOAD) { + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } + res = SPIFFS_OK; + cur_entry++; + entry_count--; + continue; + } else { + return res; + } + } else { + return SPIFFS_OK; + } + } + entry_count--; + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_VIS_END; + } else { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } + } + } // per block + + SPIFFS_CHECK_RES(res); + + return SPIFFS_VIS_END; +} + + +static s32_t ICACHE_FLASH_ATTR spiffs_obj_lu_scan_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + u32_t user_data, + void *user_p) { + if (obj_id == SPIFFS_OBJ_ID_FREE) { + if (ix_entry == 0) { + fs->free_blocks++; + // todo optimize further, return SPIFFS_NEXT_BLOCK + } + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + fs->stats_p_deleted++; + } else { + fs->stats_p_allocated++; + } + + return SPIFFS_VIS_COUNTINUE; +} + +// Scans thru all obj lu and counts free, deleted and used pages +// Find the maximum block erase count +s32_t ICACHE_FLASH_ATTR spiffs_obj_lu_scan( + spiffs *fs) { + s32_t res; + spiffs_block_ix bix; + int entry; + + fs->free_blocks = 0; + fs->stats_p_allocated = 0; + fs->stats_p_deleted = 0; + + res = spiffs_obj_lu_find_entry_visitor(fs, + 0, + 0, + 0, + 0, + spiffs_obj_lu_scan_v, + 0, + 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + SPIFFS_CHECK_RES(res); + + bix = 0; + spiffs_obj_id erase_count_final; + spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE; + spiffs_obj_id erase_count_max = 0; + while (bix < fs->block_count) { + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix) , + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + if (erase_count != SPIFFS_OBJ_ID_FREE) { + erase_count_min = MIN(erase_count_min, erase_count); + erase_count_max = MAX(erase_count_max, erase_count); + } + bix++; + } + + if (erase_count_min == 0 && erase_count_max == SPIFFS_OBJ_ID_FREE) { + // clean system, set counter to zero + erase_count_final = 0; + } else if (erase_count_max - erase_count_min > (SPIFFS_OBJ_ID_FREE)/2) { + // wrap, take min + erase_count_final = erase_count_min+1; + } else { + erase_count_final = erase_count_max+1; + } + + fs->max_erase_count = erase_count_final; + + return res; +} + +// Find free object lookup entry +// Iterate over object lookup pages in each block until a free object id entry is found +s32_t ICACHE_FLASH_ATTR spiffs_obj_lu_find_free( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_block_ix *block_ix, + int *lu_entry) { + s32_t res; + if (!fs->cleaning && fs->free_blocks < 2) { + res = spiffs_gc_quick(fs); + SPIFFS_CHECK_RES(res); + if (fs->free_blocks < 2) { + return SPIFFS_ERR_FULL; + } + } + res = spiffs_obj_lu_find_id(fs, starting_block, starting_lu_entry, + SPIFFS_OBJ_ID_FREE, block_ix, lu_entry); + if (res == SPIFFS_OK) { + fs->free_cursor_block_ix = *block_ix; + fs->free_cursor_obj_lu_entry = *lu_entry; + if (*lu_entry == 0) { + fs->free_blocks--; + } + } + if (res == SPIFFS_VIS_END) { + SPIFFS_DBG("fs full\n"); + } + + return res == SPIFFS_VIS_END ? SPIFFS_ERR_FULL : res; +} + +// Find object lookup entry containing given id +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t ICACHE_FLASH_ATTR spiffs_obj_lu_find_id( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_obj_id obj_id, + spiffs_block_ix *block_ix, + int *lu_entry) { + s32_t res = spiffs_obj_lu_find_entry_visitor( + fs, starting_block, starting_lu_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, lu_entry); + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + return res; +} + + +static s32_t ICACHE_FLASH_ATTR spiffs_obj_lu_find_id_and_span_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + u32_t user_data, + void *user_p) { + s32_t res; + spiffs_page_header ph; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + if (ph.obj_id == obj_id && + ph.span_ix == (spiffs_span_ix)user_data && + (ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET && + !((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (ph.flags & SPIFFS_PH_FLAG_IXDELE) == 0 && ph.span_ix == 0) && + (user_p == 0 || *((spiffs_page_ix *)user_p) != pix)) { + return SPIFFS_OK; + } else { + return SPIFFS_VIS_COUNTINUE; + } +} + +// Find object lookup entry containing given id and span index +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t ICACHE_FLASH_ATTR spiffs_obj_lu_find_id_and_span( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_ID, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + (u32_t)spix, + exclusion_pix ? &exclusion_pix : 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +// Find object lookup entry containing given id and span index in page headers only +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t ICACHE_FLASH_ATTR spiffs_obj_lu_find_id_and_span_by_phdr( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_PH, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + (u32_t)spix, + exclusion_pix ? &exclusion_pix : 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +// Allocates a free defined page with given obj_id +// Occupies object lookup entry and page +// data may be NULL; where only page header is stored, len and page_offs is ignored +s32_t ICACHE_FLASH_ATTR spiffs_page_allocate_data( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_page_header *ph, + u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, + spiffs_page_ix *pix) { + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + int entry; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write page header + ph->flags &= ~SPIFFS_PH_FLAG_USED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_header), (u8_t*)ph); + SPIFFS_CHECK_RES(res); + + // write page data + if (data) { + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0,SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + sizeof(spiffs_page_header) + page_offs, len, data); + SPIFFS_CHECK_RES(res); + } + + // finalize header if necessary + if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) { + ph->flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&ph->flags); + SPIFFS_CHECK_RES(res); + } + + // return written page + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; +} + +// Moves a page from src to a free page and finalizes it. Updates page index. Page data is given in param page. +// If page data is null, provided header is used for metainfo and page data is physically copied. +s32_t ICACHE_FLASH_ATTR spiffs_page_move( + spiffs *fs, + spiffs_file fh, + u8_t *page_data, + spiffs_obj_id obj_id, + spiffs_page_header *page_hdr, + spiffs_page_ix src_pix, + spiffs_page_ix *dst_pix) { + s32_t res; + u8_t was_final = 0; + spiffs_page_header *p_hdr; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + + if (dst_pix) *dst_pix = free_pix; + + p_hdr = page_data ? (spiffs_page_header *)page_data : page_hdr; + if (page_data) { + // got page data + was_final = (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) == 0; + // write unfinalized page + p_hdr->flags |= SPIFFS_PH_FLAG_FINAL; + p_hdr->flags &= ~SPIFFS_PH_FLAG_USED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), page_data); + } else { + // copy page data + res = spiffs_phys_cpy(fs, fh, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_PAGE_TO_PADDR(fs, src_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs)); + } + SPIFFS_CHECK_RES(res); + + // mark entry in destination object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + if (was_final) { + // mark finalized in destination page + p_hdr->flags &= ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fh, + SPIFFS_PAGE_TO_PADDR(fs, free_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr->flags); + SPIFFS_CHECK_RES(res); + } + // mark source deleted + res = spiffs_page_delete(fs, src_pix); + return res; +} + +// Deletes a page and removes it from object lookup. +s32_t ICACHE_FLASH_ATTR spiffs_page_delete( + spiffs *fs, + spiffs_page_ix pix) { + s32_t res; + spiffs_page_header hdr; + hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED); + // mark deleted entry in source object lookup + spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, + 0, + SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&d_obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_deleted++; + fs->stats_p_allocated--; + + // mark deleted in source page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, + 0, + SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&hdr.flags); + + return res; +} + +// Create an object index header page with empty index and undefined length +s32_t ICACHE_FLASH_ATTR spiffs_object_create( + spiffs *fs, + spiffs_obj_id obj_id, + u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_obj_type type, + spiffs_page_ix *objix_hdr_pix) { + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + spiffs_page_object_ix_header oix_hdr; + int entry; + + res = spiffs_gc_check(fs, 0); + SPIFFS_CHECK_RES(res); + + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("create: found free page @ %04x bix:%i entry:%i\n", SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write empty object index page + oix_hdr.p_hdr.obj_id = obj_id; + oix_hdr.p_hdr.span_ix = 0; + oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED); + oix_hdr.type = type; + oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page + strncpy((char *)&oix_hdr.name, (char *)name, SPIFFS_OBJ_NAME_LEN); + + + // update page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&oix_hdr); + + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN); + + if (objix_hdr_pix) { + *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; +} + +// update object index header with any combination of name/size/index +// new_objix_hdr_data may be null, if so the object index header page is loaded +// name may be null, if so name is not changed +// size may be null, if so size is not changed +s32_t ICACHE_FLASH_ATTR spiffs_object_update_index_hdr( + spiffs *fs, + spiffs_fd *fd, + spiffs_obj_id obj_id, + spiffs_page_ix objix_hdr_pix, + u8_t *new_objix_hdr_data, + u8_t name[SPIFFS_OBJ_NAME_LEN], + u32_t size, + spiffs_page_ix *new_pix) { + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header *objix_hdr; + spiffs_page_ix new_objix_hdr_pix; + + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + if (new_objix_hdr_data) { + // object index header page already given to us, no need to load it + objix_hdr = (spiffs_page_object_ix_header *)new_objix_hdr_data; + } else { + // read object index header page + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + objix_hdr = (spiffs_page_object_ix_header *)fs->work; + } + + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, obj_id, 0); + + // change name + if (name) { + strncpy((char *)objix_hdr->name, (char *)name, SPIFFS_OBJ_NAME_LEN); + } + if (size) { + objix_hdr->size = size; + } + + // move and update page + res = spiffs_page_move(fs, fd == 0 ? 0 : fd->file_nbr, (u8_t*)objix_hdr, obj_id, 0, objix_hdr_pix, &new_objix_hdr_pix); + + if (res == SPIFFS_OK) { + if (new_pix) { + *new_pix = new_objix_hdr_pix; + } + // callback on object index update + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size); + if (fd) fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster + } + + return res; +} + +void ICACHE_FLASH_ATTR spiffs_cb_object_event( + spiffs *fs, + spiffs_fd *fd, + int ev, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix new_pix, + u32_t new_size) { + // update index caches in all file descriptors + obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + int i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; + if (spix == 0) { + if (ev == SPIFFS_EV_IX_NEW || ev == SPIFFS_EV_IX_UPD) { + SPIFFS_DBG(" callback: setting fd %i:%04x objix_hdr_pix to %04x, size:%i\n", cur_fd->file_nbr, cur_fd->obj_id, new_pix, new_size); + cur_fd->objix_hdr_pix = new_pix; + if (new_size != 0) { + cur_fd->size = new_size; + } + } else if (ev == SPIFFS_EV_IX_DEL) { + cur_fd->file_nbr = 0; + cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED; + } + } + if (cur_fd->cursor_objix_spix == spix) { + if (ev == SPIFFS_EV_IX_NEW || ev == SPIFFS_EV_IX_UPD) { + SPIFFS_DBG(" callback: setting fd %i:%04x span:%04x objix_pix to %04x\n", cur_fd->file_nbr, cur_fd->obj_id, spix, new_pix); + cur_fd->cursor_objix_pix = new_pix; + } else { + cur_fd->cursor_objix_pix = 0; + } + } + } +} + +// Open object by id +s32_t ICACHE_FLASH_ATTR spiffs_object_open_by_id( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode) { + s32_t res = SPIFFS_OK; + spiffs_page_ix pix; + + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + SPIFFS_CHECK_RES(res); + + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); + + return res; +} + +// Open object by page index +s32_t ICACHE_FLASH_ATTR spiffs_object_open_by_page( + spiffs *fs, + spiffs_page_ix pix, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode) { + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header oix_hdr; + spiffs_obj_id obj_id; + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); + SPIFFS_CHECK_RES(res); + + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(fs, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix); + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); + + fd->fs = fs; + fd->objix_hdr_pix = pix; + fd->size = oix_hdr.size; + fd->offset = 0; + fd->cursor_objix_pix = pix; + fd->cursor_objix_spix = 0; + fd->obj_id = obj_id; + fd->flags = flags; + + SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); + + SPIFFS_DBG("open: fd %i is obj id %04x\n", fd->file_nbr, fd->obj_id); + + return res; +} + +// Append to object +// keep current object index (header) page in fs->work buffer +s32_t ICACHE_FLASH_ATTR spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + res = spiffs_gc_check(fs, len); + SPIFFS_CHECK_RES(res); + + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_page; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_page; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index page, unless first pass + SPIFFS_DBG("append: %04x store objix %04x:%04x, written %i\n", fd->obj_id, + cur_objix_pix, prev_objix_spix, written); + if (prev_objix_spix == 0) { + // this is an update to object index header page + objix_hdr->size = offset+written; + if (offset == 0) { + // was an empty object, update same page (size was 0xffffffff) + res = spiffs_page_index_check(fs, fd, cur_objix_pix, 0); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + } else { + // was a nonempty object, update to new page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, offset+written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: %04x store new objix_hdr, %04x:%04x, written %i\n", fd->obj_id, + new_objix_hdr_page, 0, written); + } + } else { + // this is an update to an object index page + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD,fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + // update length in object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: %04x store new size I %i in objix_hdr, %04x:%04x, written %i\n", fd->obj_id, + offset+written, new_objix_hdr_page, 0, written); + } + fd->size = offset+written; + fd->offset = offset+written; + } + + // create or load new object index page + if (cur_objix_spix == 0) { + // load object index header page, must always exist + SPIFFS_DBG("append: %04x load objixhdr page %04x:%04x\n", fd->obj_id, cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + spiffs_span_ix len_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, (fd->size-1)/SPIFFS_DATA_PAGE_SIZE(fs)); + // on subsequent passes, create a new object index page + if (written > 0 || cur_objix_spix > len_objix_spix) { + p_hdr.obj_id = fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = cur_objix_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + res = spiffs_page_allocate_data(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 1, &cur_objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); + // quick "load" of new object index page + c_memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + c_memcpy(fs->work, &p_hdr, sizeof(spiffs_page_header)); + SPIFFS_DBG("append: %04x create objix page, %04x:%04x, written %i\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + } else { + // on first pass, we load existing object index page + spiffs_page_ix pix; + SPIFFS_DBG("append: %04x find objix span_ix:%04x\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("append: %04x found object index at page %04x\n", fd->obj_id, pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset+written; + fd->size = offset+written; + } + prev_objix_spix = cur_objix_spix; + } + + // write data + u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + if (page_offs == 0) { + // at beginning of a page, allocate and write a new page of data + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_page); + SPIFFS_DBG("append: %04x store new data page, %04x:%04x offset:%i, len %i, written %i\n", fd->obj_id, + data_page, data_spix, page_offs, to_write, written); + } else { + // append to existing page, fill out free data in existing page + if (cur_objix_spix == 0) { + // get data page from object index header page + data_page = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_page = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + res = spiffs_page_data_check(fs, fd, data_page, data_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + SPIFFS_DBG("append: %04x store to existing data page, %04x:%04x offset:%i, len %i, written %i\n", fd->obj_id + , data_page, data_spix, page_offs, to_write, written); + } + + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page; + SPIFFS_DBG("append: %04x wrote page %04x to objix_hdr entry %02x in mem\n", fd->obj_id + , data_page, data_spix); + objix_hdr->size = offset+written; + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page; + SPIFFS_DBG("append: %04x wrote page %04x to objix entry %02x in mem\n", fd->obj_id + , data_page, SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->size = offset+written; + fd->offset = offset+written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page, unless object header index page + SPIFFS_DBG("append: %04x store objix page, %04x:%04x, written %i\n", fd->obj_id, + cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + + // update size in object header index page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_DBG("append: %04x store new size II %i in objix_hdr, %04x:%04x, written %i\n", fd->obj_id + , offset+written, new_objix_hdr_page, 0, written); + SPIFFS_CHECK_RES(res2); + } else { + // wrote within object index header page + if (offset == 0) { + // wrote to empty object - simply update size and write whole page + objix_hdr->size = offset+written; + SPIFFS_DBG("append: %04x store fresh objix_hdr page, %04x:%04x, written %i\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + // callback on object index update + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size); + } else { + // modifying object index header page, update size and make new copy + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, offset+written, &new_objix_hdr_page); + SPIFFS_DBG("append: %04x store modified objix_hdr page, %04x:%04x, written %i\n", fd->obj_id + , new_objix_hdr_page, 0, written); + SPIFFS_CHECK_RES(res2); + } + } + + return res; +} + +// Modify object +// keep current object index (header) page in fs->work buffer +s32_t ICACHE_FLASH_ATTR spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + res = spiffs_gc_check(fs, len); + SPIFFS_CHECK_RES(res); + + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_pix; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_pix; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index (header) page, unless first pass + if (prev_objix_spix == 0) { + // store previous object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %i\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res); + } else { + // store new version of previous object index page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store previous modified objix page, %04x:%04x, written %i\n", new_objix_pix, objix->p_hdr.span_ix, written); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + + // load next object index page + if (cur_objix_spix == 0) { + // load object index header page, must exist + SPIFFS_DBG("modify: load objixhdr page %04x:%04x\n", cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + // load existing object index page on first pass + spiffs_page_ix pix; + SPIFFS_DBG("modify: find objix span_ix:%04x\n", cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("modify: found object index at page %04x\n", pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset+written; + prev_objix_spix = cur_objix_spix; + } + + // write partial data + u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + spiffs_page_ix orig_data_pix; + if (cur_objix_spix == 0) { + // get data page from object index header page + orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs)) { + // a full page, allocate and write a new page of data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_pix); + SPIFFS_DBG("modify: store new data page, %04x:%04x offset:%i, len %i, written %i\n", data_pix, data_spix, page_offs, to_write, written); + } else { + // write to existing page, allocate new and copy unmodified data + + res = spiffs_page_data_check(fs, fd, orig_data_pix, data_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &data_pix); + if (res != SPIFFS_OK) break; + + // copy unmodified data + if (page_offs > 0) { + // before modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header), + page_offs); + if (res != SPIFFS_OK) break; + } + if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs)) { + // after modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_DATA_PAGE_SIZE(fs) - (page_offs + to_write)); + if (res != SPIFFS_OK) break; + } + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + SPIFFS_DBG("modify: store to existing data page, src:%04x, dst:%04x:%04x offset:%i, len %i, written %i\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); + } + + // delete original data page + res = spiffs_page_delete(fs, orig_data_pix); + if (res != SPIFFS_OK) break; + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix; + SPIFFS_DBG("modify: wrote page %04x to objix_hdr entry %02x in mem\n", data_pix, data_spix); + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix; + SPIFFS_DBG("modify: wrote page %04x to objix entry %02x in mem\n", data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->offset = offset+written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page + // move and update page + spiffs_page_ix new_objix_pix; + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store modified objix page, %04x:%04x, written %i\n", new_objix_pix, cur_objix_spix, written); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + + } else { + // wrote within object index header page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %i\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res2); + } + + return res; +} + +static s32_t ICACHE_FLASH_ATTR spiffs_object_find_object_index_header_by_name_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + u32_t user_data, + void *user_p) { + s32_t res; + spiffs_page_object_ix_header objix_hdr; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + if (strcmp((char *)user_p, (char *)objix_hdr.name) == 0) { + return SPIFFS_OK; + } + } + + return SPIFFS_VIS_COUNTINUE; +} + +// Finds object index header page by name +s32_t ICACHE_FLASH_ATTR spiffs_object_find_object_index_header_by_name( + spiffs *fs, + u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + 0, + 0, + spiffs_object_find_object_index_header_by_name_v, + 0, + name, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +// Truncates object to new size. If new size is null, object may be removed totally +s32_t ICACHE_FLASH_ATTR spiffs_object_truncate( + spiffs_fd *fd, + u32_t new_size, + u8_t remove) { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + + res = spiffs_gc_check(fs, 0); + SPIFFS_CHECK_RES(res); + + spiffs_page_ix objix_pix = fd->objix_hdr_pix; + spiffs_span_ix data_spix = (fd->size > 0 ? fd->size-1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_size = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size ; + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_ix data_pix; + spiffs_page_ix new_objix_hdr_pix; + + // before truncating, check if object is to be fully removed and mark this + if (remove && new_size == 0) { + u8_t flags = ~( SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&flags); + SPIFFS_CHECK_RES(res); + } + + // delete from end of object until desired len is reached + while (cur_size > new_size) { + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // put object index for current data span index in work buffer + if (prev_objix_spix != cur_objix_spix) { + if (prev_objix_spix != (spiffs_span_ix)-1) { + // remove previous object index page + SPIFFS_DBG("truncate: delete objix page %04x:%04x\n", objix_pix, prev_objix_spix); + + res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0); + if (prev_objix_spix > 0) { + // update object index header page + SPIFFS_DBG("truncate: update objix hdr page %04x:%04x to size %i\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + fd->size = cur_size; + } + } + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + + SPIFFS_DBG("truncate: load objix page %04x:%04x for data spix:%04x\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = SPIFFS_OBJ_ID_FREE; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; + } + + if (cur_size - SPIFFS_DATA_PAGE_SIZE(fs) >= new_size) { + // delete full data page + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_OK) break; + + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) break; + // update current size + if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) { + cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); + } else { + cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs); + } + fd->size = cur_size; + fd->offset = cur_size; + SPIFFS_DBG("truncate: delete data page %04x for data spix:%04x, cur_size:%i\n", data_pix, data_spix, cur_size); + } else { + // delete last page, partially + spiffs_page_header p_hdr; + spiffs_page_ix new_data_pix; + u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_DBG("truncate: delete %i bytes from data page %04x for data spix:%04x, cur_size:%i\n", bytes_to_remove, data_pix, data_spix, cur_size); + + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_OK) break; + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + // allocate new page and copy unmodified data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &new_data_pix); + if (res != SPIFFS_OK) break; + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove); + if (res != SPIFFS_OK) break; + // delete original data page + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + SPIFFS_DBG("truncate: wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } else { + // update object index page + ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + SPIFFS_DBG("truncate: wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + cur_size = new_size; + fd->size = new_size; + fd->offset = cur_size; + break; + } + data_spix--; + } // while all data + + // update object indices + if (cur_objix_spix == 0) { + // update object index header page + if (cur_size == 0) { + if (remove) { + // remove object altogether + SPIFFS_DBG("truncate: remove object index header page %04x\n", objix_pix); + + res = spiffs_page_index_check(fs, fd, objix_pix, 0); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0); + } else { + // make uninitialized object + SPIFFS_DBG("truncate: reset objix_hdr page %04x\n", objix_pix); + c_memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update object index header page + SPIFFS_DBG("truncate: update object index header page with indices and size\n"); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update both current object index page and object index header page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res); + + // move and update object index page + res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix_hdr, fd->obj_id, 0, objix_pix, &new_objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + SPIFFS_DBG("truncate: store modified objix page, %04x:%04x\n", new_objix_pix, cur_objix_spix); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + // update object index header page with new size + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + + return res; +} + +s32_t ICACHE_FLASH_ATTR spiffs_object_read( + spiffs_fd *fd, + u32_t offset, + u32_t len, + u8_t *dst) { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + spiffs_page_ix objix_pix; + spiffs_page_ix data_pix; + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_offset = offset; + spiffs_span_ix cur_objix_spix; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + + while (cur_offset < offset + len) { + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (prev_objix_spix != cur_objix_spix) { + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + SPIFFS_DBG("read: find objix %04x:%04x\n", fd->obj_id, cur_objix_spix); + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("read: load objix page %04x:%04x for data spix:%04x\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix); + + fd->offset = cur_offset; + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + // all remaining data + u32_t len_to_read = offset + len - cur_offset; + // remaining data in page + len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); + // remaining data in file + len_to_read = MIN(len_to_read, fd->size); + SPIFFS_DBG("read: offset:%i rd:%i data spix:%04x is data_pix:%04x addr:%08x\n", cur_offset, len_to_read, data_spix, data_pix, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); + if (len_to_read <= 0) { + res = SPIFFS_ERR_END_OF_OBJECT; + break; + } + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + SPIFFS_CHECK_RES(res); + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)), + len_to_read, + dst); + SPIFFS_CHECK_RES(res); + dst += len_to_read; + cur_offset += len_to_read; + fd->offset = cur_offset; + data_spix++; + } + + return res; +} + +typedef struct { + spiffs_obj_id min_obj_id; + spiffs_obj_id max_obj_id; + u32_t compaction; +} spiffs_free_obj_id_state; + +static s32_t ICACHE_FLASH_ATTR spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + u32_t user_data, void *user_p) { + if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED) { + spiffs_obj_id min_obj_id = user_data; + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + int bit_ix = (id-min_obj_id) & 7; + int byte_ix = (id-min_obj_id) >> 3; + if (byte_ix >= 0 && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + fs->work[byte_ix] |= (1<= state->min_obj_id && id <= state->max_obj_id) { + u8_t *map = (u8_t *)fs->work; + int ix = (id - state->min_obj_id) / state->compaction; + //SPIFFS_DBG("free_obj_id: add ix %i for id %04x min:%04x max%04x comp:%i\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); + map[ix]++; + } + } + } + return SPIFFS_VIS_COUNTINUE; +} + +// Scans thru all object lookup for object index header pages. If total possible number of +// object ids cannot fit into a work buffer, these are grouped. When a group containing free +// object ids is found, the object lu is again scanned for object ids within group and bitmasked. +// Finally, the bitmasked is searched for a free id +s32_t ICACHE_FLASH_ATTR spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id) { + s32_t res = SPIFFS_OK; + u32_t max_objects = (SPIFFS_CFG_PHYS_SZ(fs) / (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) / 2; + spiffs_free_obj_id_state state; + spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE; + state.min_obj_id = 1; + state.max_obj_id = max_objects + 1; + if (state.max_obj_id & SPIFFS_OBJ_ID_IX_FLAG) { + state.max_obj_id = ((spiffs_obj_id)-1) & ~SPIFFS_OBJ_ID_IX_FLAG; + } + state.compaction = 0; + while (res == SPIFFS_OK && free_obj_id == SPIFFS_OBJ_ID_FREE) { + if (state.max_obj_id - state.min_obj_id <= SPIFFS_CFG_LOG_PAGE_SZ(fs)*8) { + // possible to represent in bitmap + int i, j; + SPIFFS_DBG("free_obj_id: BITM min:%04x max:%04x\n", state.min_obj_id, state.max_obj_id); + + c_memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, state.min_obj_id, 0, 0, 0); + if (res == SPIFFS_VIS_END) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + // traverse bitmask until found free obj_id + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) { + u8_t mask = fs->work[i]; + if (mask == 0xff) { + continue; + } + for (j = 0; j < 8; j++) { + if ((mask & (1<work; + u8_t min_count = 0xff; + + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(u8_t); i++) { + if (map[i] < min_count) { + min_count = map[i]; + min_i = i; + if (min_count == 0) { + break; + } + } + } + + if (min_count == state.compaction) { + // there are no free objids! + SPIFFS_DBG("free_obj_id: compacted table is full\n"); + return SPIFFS_ERR_FULL; + } + + SPIFFS_DBG("free_obj_id: COMP select index:%i min_count:%i min:%04x max:%04x compact:%i\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); + + if (min_count == 0) { + // no id in this range, skip compacting and use directly + *obj_id = min_i * state.compaction + state.min_obj_id; + return SPIFFS_OK; + } else { + SPIFFS_DBG("free_obj_id: COMP SEL chunk:%04x min:%04x -> %04x\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction); + state.min_obj_id += min_i * state.compaction; + state.max_obj_id = state.min_obj_id + state.compaction; + // decrease compaction + } + if ((state.max_obj_id - state.min_obj_id <= SPIFFS_CFG_LOG_PAGE_SZ(fs)*8)) { + // no need for compacting, use bitmap + continue; + } + } + // in a work memory of log_page_size bytes, we may fit in log_page_size ids + // todo what if compaction is > 255 - then we cannot fit it in a byte + state.compaction = (state.max_obj_id-state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t))); + SPIFFS_DBG("free_obj_id: COMP min:%04x max:%04x compact:%i\n", state.min_obj_id, state.max_obj_id, state.compaction); + + c_memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, 0, &state, 0, 0); + if (res == SPIFFS_VIS_END) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + } + } + + return res; +} + +s32_t ICACHE_FLASH_ATTR spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd) { + int i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + cur_fd->file_nbr = i+1; + *fd = cur_fd; + return SPIFFS_OK; + } + } + return SPIFFS_ERR_OUT_OF_FILE_DESCS; +} + +s32_t ICACHE_FLASH_ATTR spiffs_fd_return(spiffs *fs, spiffs_file f) { + if (f <= 0 || f > fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + spiffs_fd *fd = &fds[f-1]; + if (fd->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + fd->file_nbr = 0; + return SPIFFS_OK; +} + +s32_t ICACHE_FLASH_ATTR spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) { + if (f <= 0 || f > fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + *fd = &fds[f-1]; + if ((*fd)->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + return SPIFFS_OK; +} diff --git a/app/spiffs/spiffs_nucleus.h b/app/spiffs/spiffs_nucleus.h new file mode 100644 index 00000000..6df5f4e0 --- /dev/null +++ b/app/spiffs/spiffs_nucleus.h @@ -0,0 +1,684 @@ +/* + * spiffs_nucleus.h + * + * Created on: Jun 15, 2013 + * Author: petera + */ + +/* SPIFFS layout + * + * spiffs is designed for following spi flash characteristics: + * - only big areas of data (blocks) can be erased + * - erasing resets all bits in a block to ones + * - writing pulls ones to zeroes + * - zeroes cannot be pulled to ones, without erase + * - wear leveling + * + * spiffs is also meant to be run on embedded, memory constraint devices. + * + * Entire area is divided in blocks. Entire area is also divided in pages. + * Each block contains same number of pages. A page cannot be erased, but a + * block can be erased. + * + * Entire area must be block_size * x + * page_size must be block_size / (2^y) where y > 2 + * + * ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes + * + * BLOCK 0 PAGE 0 object lookup 1 + * PAGE 1 object lookup 2 + * ... + * PAGE n-1 object lookup n + * PAGE n object data 1 + * PAGE n+1 object data 2 + * ... + * PAGE n+m-1 object data m + * + * BLOCK 1 PAGE n+m object lookup 1 + * PAGE n+m+1 object lookup 2 + * ... + * PAGE 2n+m-1 object lookup n + * PAGE 2n+m object data 1 + * PAGE 2n+m object data 2 + * ... + * PAGE 2n+2m-1 object data m + * ... + * + * n is number of object lookup pages, which is number of pages needed to index all pages + * in a block by object id + * : block_size / page_size * sizeof(obj_id) / page_size + * m is number data pages, which is number of pages in block minus number of lookup pages + * : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size + * thus, n+m is total number of pages in a block + * : block_size / page_size + * + * ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256 + * + * Object lookup pages contain object id entries. Each entry represent the corresponding + * data page. + * Assuming a 16 bit object id, an object id being 0xffff represents a free page. + * An object id being 0x0000 represents a deleted page. + * + * ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff .. + * page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff .. + * page 2 : data : data for object id 0008 + * page 3 : data : data for object id 0001 + * page 4 : data : data for object id 0aaa + * ... + * + * + * Object data pages can be either object index pages or object content. + * All object data pages contains a data page header, containing object id and span index. + * The span index denotes the object page ordering amongst data pages with same object id. + * This applies to both object index pages (when index spans more than one page of entries), + * and object data pages. + * An object index page contains page entries pointing to object content page. The entry index + * in a object index page correlates to the span index in the actual object data page. + * The first object index page (span index 0) is called object index header page, and also + * contains object flags (directory/file), size, object name etc. + * + * ex: + * BLOCK 1 + * PAGE 256: objectl lookup page 1 + * [*123] [ 123] [ 123] [ 123] + * [ 123] [*123] [ 123] [ 123] + * [free] [free] [free] [free] ... + * PAGE 257: objectl lookup page 2 + * [free] [free] [free] [free] ... + * PAGE 258: object index page (header) + * obj.id:0123 span.ix:0000 flags:INDEX + * size:1600 name:ex.txt type:file + * [259] [260] [261] [262] + * PAGE 259: object data page + * obj.id:0123 span.ix:0000 flags:DATA + * PAGE 260: object data page + * obj.id:0123 span.ix:0001 flags:DATA + * PAGE 261: object data page + * obj.id:0123 span.ix:0002 flags:DATA + * PAGE 262: object data page + * obj.id:0123 span.ix:0003 flags:DATA + * PAGE 263: object index page + * obj.id:0123 span.ix:0001 flags:INDEX + * [264] [265] [fre] [fre] + * [fre] [fre] [fre] [fre] + * PAGE 264: object data page + * obj.id:0123 span.ix:0004 flags:DATA + * PAGE 265: object data page + * obj.id:0123 span.ix:0005 flags:DATA + * + */ +#ifndef SPIFFS_NUCLEUS_H_ +#define SPIFFS_NUCLEUS_H_ + +#define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1) +#define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1) +#define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2) +#define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3) +#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4) + +#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20) +#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21) +#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22) + +#define SPIFFS_EV_IX_UPD 0 +#define SPIFFS_EV_IX_NEW 1 +#define SPIFFS_EV_IX_DEL 2 + +#define SPIFFS_OBJ_ID_IX_FLAG (1<<(8*sizeof(spiffs_obj_id)-1)) + +#define SPIFFS_UNDEFINED_LEN (-1) + +#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0) +#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) + +#if SPIFFS_SINGLETON == 0 +#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \ + ((fs)->cfg.log_page_size) +#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \ + ((fs)->cfg.log_block_size) +#define SPIFFS_CFG_PHYS_SZ(fs) \ + ((fs)->cfg.phys_size) +#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \ + ((fs)->cfg.phys_erase_block) +#define SPIFFS_CFG_PHYS_ADDR(fs) \ + ((fs)->cfg.phys_addr) +#endif + +// total number of pages +#define SPIFFS_MAX_PAGES(fs) \ + ( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// total number of pages per block, including object lookup pages +#define SPIFFS_PAGES_PER_BLOCK(fs) \ + ( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// number of object lookup pages per block +#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \ + (MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) ) +// checks if page index belongs to object lookup +#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \ + (((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) +// number of object lookup entries in all object lookup pages +#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \ + (SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) +// converts a block to physical address +#define SPIFFS_BLOCK_TO_PADDR(fs, block) \ + ( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) ) +// converts a object lookup entry to page index +#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \ + ((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry)) +// converts a object lookup entry to physical address of corresponding page +#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \ + (SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// converts a page to physical address +#define SPIFFS_PAGE_TO_PADDR(fs, page) \ + ( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// converts a physical address to page +#define SPIFFS_PADDR_TO_PAGE(fs, addr) \ + ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// gives index in page for a physical address +#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \ + ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// returns containing block for given page +#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \ + ( (page) / SPIFFS_PAGES_PER_BLOCK(fs) ) +// returns starting page for block +#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \ + ( (block) * SPIFFS_PAGES_PER_BLOCK(fs) ) +// converts page to entry in object lookup page +#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \ + ( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) ) +// returns data size in a data page +#define SPIFFS_DATA_PAGE_SIZE(fs) \ + ( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) ) +// returns physical address for block's erase count +#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \ + ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) ) + +// define helpers object + +// entries in an object header page index +#define SPIFFS_OBJ_HDR_IX_LEN(fs) \ + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix)) +// entries in an object page index +#define SPIFFS_OBJ_IX_LEN(fs) \ + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix)) +// object index entry for given data span index +#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \ + ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs))) +// object index span index number for given data span index or entry +#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \ + ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs))) + + +#define SPIFFS_OP_T_OBJ_LU (0<<0) +#define SPIFFS_OP_T_OBJ_LU2 (1<<0) +#define SPIFFS_OP_T_OBJ_IX (2<<0) +#define SPIFFS_OP_T_OBJ_DA (3<<0) +#define SPIFFS_OP_C_DELE (0<<2) +#define SPIFFS_OP_C_UPDT (1<<2) +#define SPIFFS_OP_C_MOVS (2<<2) +#define SPIFFS_OP_C_MOVD (3<<2) +#define SPIFFS_OP_C_FLSH (4<<2) +#define SPIFFS_OP_C_READ (5<<2) +#define SPIFFS_OP_C_WRTHRU (6<<2) + +#define SPIFFS_OP_TYPE_MASK (3<<0) +#define SPIFFS_OP_COM_MASK (7<<2) + + +// if 0, this page is written to, else clean +#define SPIFFS_PH_FLAG_USED (1<<0) +// if 0, writing is finalized, else under modification +#define SPIFFS_PH_FLAG_FINAL (1<<1) +// if 0, this is an index page, else a data page +#define SPIFFS_PH_FLAG_INDEX (1<<2) +// if 0, page is deleted, else valid +#define SPIFFS_PH_FLAG_DELET (1<<7) +// if 0, this index header is being deleted +#define SPIFFS_PH_FLAG_IXDELE (1<<6) + + +#define SPIFFS_CHECK_MOUNT(fs) \ + ((fs)->block_count > 0) + +#define SPIFFS_CHECK_RES(res) \ + do { \ + if ((res) < SPIFFS_OK) return (res); \ + } while (0); + +#define SPIFFS_API_CHECK_MOUNT(fs) \ + if (!SPIFFS_CHECK_MOUNT((fs))) { \ + (fs)->errno = SPIFFS_ERR_NOT_MOUNTED; \ + return -1; \ + } + +#define SPIFFS_API_CHECK_RES(fs, res) \ + if ((res) < SPIFFS_OK) { \ + (fs)->errno = (res); \ + return -1; \ + } + +#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ + if ((res) < SPIFFS_OK) { \ + (fs)->errno = (res); \ + SPIFFS_UNLOCK(fs); \ + return -1; \ + } + +#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \ + if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ + if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ + if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ + if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \ + if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \ + if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH; + //if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED; + +#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \ + if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ + if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ + if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ + if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \ + if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \ + if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH; + + +// check id +#define SPIFFS_VIS_CHECK_ID (1<<0) +// report argument object id to visitor - else object lookup id is reported +#define SPIFFS_VIS_CHECK_PH (1<<1) +// stop searching at end of all look up pages +#define SPIFFS_VIS_NO_WRAP (1<<2) + +#if SPIFFS_CACHE + +#define SPIFFS_CACHE_FLAG_DIRTY (1<<0) +#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1) +#define SPIFFS_CACHE_FLAG_OBJLU (1<<2) +#define SPIFFS_CACHE_FLAG_OBJIX (1<<3) +#define SPIFFS_CACHE_FLAG_DATA (1<<4) +#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7) + +#define SPIFFS_CACHE_PAGE_SIZE(fs) \ + (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)) + +#define spiffs_get_cache(fs) \ + ((spiffs_cache *)((fs)->cache)) + +#define spiffs_get_cache_page_hdr(fs, c, ix) \ + ((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)]))) + +#define spiffs_get_cache_page(fs, c, ix) \ + ((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page)) + +// cache page struct +typedef struct { + // cache flags + u8_t flags; + // cache page index + u8_t ix; + // last access of this cache page + u32_t last_access; + union { + // type read cache + struct { + // read cache page index + spiffs_page_ix pix; + }; +#if SPIFFS_CACHE_WR + // type write cache + struct { + // write cache + spiffs_obj_id obj_id; + // offset in cache page + u32_t offset; + // size of cache page + u16_t size; + }; +#endif + }; +} spiffs_cache_page; + +// cache struct +typedef struct { + u8_t cpage_count; + u32_t last_access; + u32_t cpage_use_map; + u32_t cpage_use_mask; + u8_t *cpages; +} spiffs_cache; + +#endif + + +// spiffs nucleus file descriptor +typedef struct { + // the filesystem of this descriptor + spiffs *fs; + // number of file descriptor - if 0, the file descriptor is closed + spiffs_file file_nbr; + // object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted + spiffs_obj_id obj_id; + // size of the file + u32_t size; + // cached object index header page index + spiffs_page_ix objix_hdr_pix; + // cached offset object index page index + spiffs_page_ix cursor_objix_pix; + // cached offset object index span index + spiffs_span_ix cursor_objix_spix; + // current absolute offset + u32_t offset; + // current file descriptor offset + u32_t fdoffset; + // fd flags + spiffs_flags flags; +#if SPIFFS_CACHE_WR + spiffs_cache_page *cache_page; +#endif +} spiffs_fd; + + +// object structs + +// page header, part of each page except object lookup pages +typedef struct __attribute(( packed )) { + // object id + spiffs_obj_id obj_id; + // object span index + spiffs_span_ix span_ix; + // flags + u8_t flags; +} spiffs_page_header; + +// object index header page header +typedef struct __attribute(( packed )) { + // common page header + spiffs_page_header p_hdr; + // alignment + u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)]; + // size of object + u32_t size; + // type of object + spiffs_obj_type type; + // name of object + u8_t name[SPIFFS_OBJ_NAME_LEN]; +} spiffs_page_object_ix_header; + +// object index page header +typedef struct __attribute(( packed )) { + spiffs_page_header p_hdr; + u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)]; +} spiffs_page_object_ix; + +// callback func for object lookup visitor +typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + u32_t user_data, void *user_p); + + +#if SPIFFS_CACHE +#define _spiffs_rd(fs, op, fh, addr, len, dst) \ + spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst)) +#define _spiffs_wr(fs, op, fh, addr, len, src) \ + spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src)) +#else +#define _spiffs_rd(fs, op, fh, addr, len, dst) \ + spiffs_phys_rd((fs), (addr), (len), (dst)) +#define _spiffs_wr(fs, op, fh, addr, len, src) \ + spiffs_phys_wr((fs), (addr), (len), (src)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +// --------------- + +s32_t spiffs_phys_rd( + spiffs *fs, +#if SPIFFS_CACHE + u8_t op, + spiffs_file fh, +#endif + u32_t addr, + u32_t len, + u8_t *dst); + +s32_t spiffs_phys_wr( + spiffs *fs, +#if SPIFFS_CACHE + u8_t op, + spiffs_file fh, +#endif + u32_t addr, + u32_t len, + u8_t *src); + +s32_t spiffs_phys_cpy( + spiffs *fs, + spiffs_file fh, + u32_t dst, + u32_t src, + u32_t len); + +s32_t spiffs_phys_count_free_blocks( + spiffs *fs); + +s32_t spiffs_obj_lu_find_entry_visitor( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + u8_t flags, + spiffs_obj_id obj_id, + spiffs_visitor_f v, + u32_t user_data, + void *user_p, + spiffs_block_ix *block_ix, + int *lu_entry); + +// --------------- + +s32_t spiffs_obj_lu_scan( + spiffs *fs); + +s32_t spiffs_obj_lu_find_free_obj_id( + spiffs *fs, + spiffs_obj_id *obj_id); + +s32_t spiffs_obj_lu_find_free( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_block_ix *block_ix, + int *lu_entry); + +s32_t spiffs_obj_lu_find_id( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_obj_id obj_id, + spiffs_block_ix *block_ix, + int *lu_entry); + +s32_t spiffs_obj_lu_find_id_and_span( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix); + +s32_t spiffs_obj_lu_find_id_and_span_by_phdr( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix); + +// --------------- + +s32_t spiffs_page_allocate_data( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_page_header *ph, + u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, + spiffs_page_ix *pix); + +s32_t spiffs_page_move( + spiffs *fs, + spiffs_file fh, + u8_t *page_data, + spiffs_obj_id obj_id, + spiffs_page_header *page_hdr, + spiffs_page_ix src_pix, + spiffs_page_ix *dst_pix); + +s32_t spiffs_page_delete( + spiffs *fs, + spiffs_page_ix pix); + +// --------------- + +s32_t spiffs_object_create( + spiffs *fs, + spiffs_obj_id obj_id, + u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_obj_type type, + spiffs_page_ix *objix_hdr_pix); + +s32_t spiffs_object_update_index_hdr( + spiffs *fs, + spiffs_fd *fd, + spiffs_obj_id obj_id, + spiffs_page_ix objix_hdr_pix, + u8_t *new_objix_hdr_data, + u8_t name[SPIFFS_OBJ_NAME_LEN], + u32_t size, + spiffs_page_ix *new_pix); + +void spiffs_cb_object_event( + spiffs *fs, + spiffs_fd *fd, + int ev, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix new_pix, + u32_t new_size); + +s32_t spiffs_object_open_by_id( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_fd *f, + spiffs_flags flags, + spiffs_mode mode); + +s32_t spiffs_object_open_by_page( + spiffs *fs, + spiffs_page_ix pix, + spiffs_fd *f, + spiffs_flags flags, + spiffs_mode mode); + +s32_t spiffs_object_append( + spiffs_fd *fd, + u32_t offset, + u8_t *data, + u32_t len); + +s32_t spiffs_object_modify( + spiffs_fd *fd, + u32_t offset, + u8_t *data, + u32_t len); + +s32_t spiffs_object_read( + spiffs_fd *fd, + u32_t offset, + u32_t len, + u8_t *dst); + +s32_t spiffs_object_truncate( + spiffs_fd *fd, + u32_t new_len, + u8_t remove_object); + +s32_t spiffs_object_find_object_index_header_by_name( + spiffs *fs, + u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_page_ix *pix); + +// --------------- + +s32_t spiffs_gc_check( + spiffs *fs, + u32_t len); + +s32_t spiffs_gc_erase_page_stats( + spiffs *fs, + spiffs_block_ix bix); + +s32_t spiffs_gc_find_candidate( + spiffs *fs, + spiffs_block_ix **block_candidate, + int *candidate_count); + +s32_t spiffs_gc_clean( + spiffs *fs, + spiffs_block_ix bix); + +s32_t spiffs_gc_quick( + spiffs *fs); + +// --------------- + +s32_t spiffs_fd_find_new( + spiffs *fs, + spiffs_fd **fd); + +s32_t spiffs_fd_return( + spiffs *fs, + spiffs_file f); + +s32_t spiffs_fd_get( + spiffs *fs, + spiffs_file f, + spiffs_fd **fd); + +#if SPIFFS_CACHE +void spiffs_cache_init( + spiffs *fs); + +void spiffs_cache_drop_page( + spiffs *fs, + spiffs_page_ix pix); + +#if SPIFFS_CACHE_WR +spiffs_cache_page *spiffs_cache_page_allocate_by_fd( + spiffs *fs, + spiffs_fd *fd); + +void spiffs_cache_fd_release( + spiffs *fs, + spiffs_cache_page *cp); + +spiffs_cache_page *spiffs_cache_page_get_by_fd( + spiffs *fs, + spiffs_fd *fd); +#endif +#endif + +s32_t spiffs_lookup_consistency_check( + spiffs *fs, + u8_t check_all_objects); + +s32_t spiffs_page_consistency_check( + spiffs *fs); + +s32_t spiffs_object_index_consistency_check( + spiffs *fs); + +#endif /* SPIFFS_NUCLEUS_H_ */ diff --git a/app/ssl/Makefile b/app/ssl/Makefile new file mode 100644 index 00000000..5308f085 --- /dev/null +++ b/app/ssl/Makefile @@ -0,0 +1,48 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +UP_EXTRACT_DIR = .. +GEN_LIBS = libssl.a +COMPONENTS_libssl = crypto/libsslcrypto.a \ + ssl/libsslssl.a \ + app/libsslapp.a +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/ssl/app/Makefile b/app/ssl/app/Makefile new file mode 100644 index 00000000..15cb23e1 --- /dev/null +++ b/app/ssl/app/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = libsslapp.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/ssl/app/espconn_secure.c b/app/ssl/app/espconn_secure.c new file mode 100644 index 00000000..ec679f32 --- /dev/null +++ b/app/ssl/app/espconn_secure.c @@ -0,0 +1,97 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: espconn_encry.c + * + * Description: data encrypt interface + * + * Modification history: + * 2014/3/31, v1.0 create this file. +*******************************************************************************/ + +#include "lwip/netif.h" +#include "lwip/inet.h" +#include "netif/etharp.h" +#include "lwip/tcp.h" +#include "lwip/ip.h" +#include "lwip/init.h" +#include "ets_sys.h" +#include "os_type.h" +//#include "os.h" + +#include "ssl/app/espconn_ssl.h" + +/****************************************************************************** + * FunctionName : espconn_encry_connect + * Description : The function given as the connect + * Parameters : espconn -- the espconn used to listen the connection + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_secure_connect(struct espconn *espconn) +{ + if (espconn == NULL) + return ESPCONN_ARG; + + return espconn_ssl_client(espconn); +} + +/****************************************************************************** + * FunctionName : espconn_encry_disconnect + * Description : The function given as the disconnect + * Parameters : espconn -- the espconn used to listen the connection + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_secure_disconnect(struct espconn *espconn) +{ + espconn_msg *pnode = NULL; + bool value = false; + if (espconn == NULL) + return ESPCONN_ARG; + + value = espconn_find_connection(espconn, &pnode); + if (value){ + espconn_ssl_disconnect(pnode); + return ESPCONN_OK; + } + else + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_encry_sent + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_secure_sent(struct espconn *espconn, uint8 *psent, uint16 length) +{ + espconn_msg *pnode = NULL; + bool value = false; + if (espconn == NULL) + return ESPCONN_ARG; + + espconn ->state = ESPCONN_WRITE; + value = espconn_find_connection(espconn, &pnode); + if (value){ + espconn_ssl_sent(pnode, psent, length); + return ESPCONN_OK; + } + else + return ESPCONN_ARG; +} + +sint8 ICACHE_FLASH_ATTR +espconn_secure_accept(struct espconn *espconn) +{ + if (espconn == NULL) + return ESPCONN_ARG; + + return espconn_ssl_server(espconn); +} + + diff --git a/app/ssl/app/espconn_ssl.c b/app/ssl/app/espconn_ssl.c new file mode 100644 index 00000000..860db844 --- /dev/null +++ b/app/ssl/app/espconn_ssl.c @@ -0,0 +1,1100 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: espconn_ssl.c + * + * Description: ssl encrypt interface + * + * Modification history: + * 2014/3/31, v1.0 create this file. +*******************************************************************************/ + +#include "lwip/netif.h" +#include "netif/etharp.h" +#include "lwip/tcp.h" +#include "lwip/ip.h" +#include "lwip/init.h" +#include "lwip/tcp_impl.h" + +#include "ssl/ssl_os_port.h" + +#include "ssl/app/espconn_ssl.h" +#include "ets_sys.h" +#include "os_type.h" +//#include "os.h" +#include "lwip/app/espconn.h" + +struct pbuf *psslpbuf = NULL; +extern espconn_msg *plink_active; + +static err_t espconn_ssl_crecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); +static err_t espconn_ssl_srecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); + +static void espconn_ssl_sclose(void *arg, struct tcp_pcb *pcb); +static void espconn_ssl_cclose(void *arg, struct tcp_pcb *pcb); + +/////////////////////////////common function/////////////////////////////////// +/****************************************************************************** + * FunctionName : display_session_id + * Description : Display what session id we have. + * Parameters : + * Returns : +*******************************************************************************/ +static void ICACHE_FLASH_ATTR display_session_id(SSL *ssl) +{ + int i; + const uint8_t *session_id = ssl_get_session_id(ssl); + int sess_id_size = ssl_get_session_id_size(ssl); + + if (sess_id_size > 0) { + ssl_printf("-----BEGIN SSL SESSION PARAMETERS-----\n"); + + for (i = 0; i < sess_id_size; i++) { + ssl_printf("%02x", session_id[i]); + } + + ssl_printf("\n-----END SSL SESSION PARAMETERS-----\n"); + //TTY_FLUSH(); + } +} + +/****************************************************************************** + * FunctionName : display_cipher + * Description : Display what cipher we are using + * Parameters : + * Returns : +*******************************************************************************/ +static void ICACHE_FLASH_ATTR display_cipher(SSL *ssl) +{ + ssl_printf("CIPHER is "); + + switch (ssl_get_cipher_id(ssl)) { + case SSL_AES128_SHA: + ssl_printf("AES128-SHA"); + break; + + case SSL_AES256_SHA: + ssl_printf("AES256-SHA"); + break; + + case SSL_RC4_128_SHA: + ssl_printf("RC4-SHA"); + break; + + case SSL_RC4_128_MD5: + ssl_printf("RC4-MD5"); + break; + + default: + ssl_printf("Unknown - %d", ssl_get_cipher_id(ssl)); + break; + } + + ssl_printf("\n"); + //TTY_FLUSH(); +} + +/****************************************************************************** + * FunctionName : espconn_ssl_reconnect + * Description : reconnect with host + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_ssl_reconnect(void *arg) +{ + espconn_msg *pssl_recon = arg; + struct espconn *espconn = NULL; + ssl_msg *pssl = NULL; + sint8 ssl_reerr = 0; + if (pssl_recon != NULL) { + espconn = pssl_recon->preverse; + if (pssl_recon->pespconn != NULL){ + if (espconn != NULL){ + /*espconn_copy_partial(espconn, pssl_recon->pespconn); + if (pssl_recon->pespconn->proto.tcp != NULL){ + os_free(pssl_recon->pespconn->proto.tcp); + pssl_recon->pespconn->proto.tcp = NULL; + } + os_free(pssl_recon->pespconn); + pssl_recon->pespconn = NULL;*/ + espconn = pssl_recon->preverse; + } else { + espconn = pssl_recon->pespconn; + } + } + pssl = pssl_recon->pssl; + ssl_reerr = pssl_recon->pcommon.err; + if (pssl != NULL) { + if (pssl->ssl) { + ssl_free(pssl->ssl); + } + + if (pssl->ssl_ctx) { + ssl_ctx_free(pssl->ssl_ctx); + } + + os_free(pssl); + pssl = NULL; + pssl_recon->pssl = pssl; + } + os_free(pssl_recon); + pssl_recon = NULL; + if (espconn ->proto.tcp->reconnect_callback != NULL) { + espconn ->proto.tcp->reconnect_callback(espconn, ssl_reerr); + } + } else { + ssl_printf("espconn_ssl_reconnect err\n"); + } +} + +/****************************************************************************** + * FunctionName : espconn_ssl_dissuccessful + * Description : as + * Parameters : + * Returns : +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_ssl_dissuccessful(void *arg) +{ + espconn_msg *pdiscon = arg; + struct espconn *espconn = NULL; + struct tcp_pcb *pcb = NULL; + ssl_msg *pssl = NULL; + if (pdiscon != NULL) { + espconn = pdiscon->preverse; + if (pdiscon->pespconn != NULL){ + if (espconn != NULL){ + /*espconn_copy_partial(espconn, pdiscon->pespconn); + if (pdiscon->pespconn->proto.tcp != NULL){ + os_free(pdiscon->pespconn->proto.tcp); + pdiscon->pespconn->proto.tcp = NULL; + } + os_free(pdiscon->pespconn); + pdiscon->pespconn = NULL;*/ + espconn = pdiscon->preverse; + } else{ + espconn = pdiscon->pespconn; + } + pcb = pdiscon->pcommon.pcb; + tcp_arg(pcb, NULL); + tcp_err(pcb, NULL); + } + + pssl = pdiscon->pssl; + + if (pssl != NULL) { + if (pssl->ssl) { + ssl_free(pssl->ssl); + } + + if (pssl->ssl_ctx) { + ssl_ctx_free(pssl->ssl_ctx); + } + + os_free(pssl); + pssl = NULL; + pdiscon->pssl = pssl; + } + + os_free(pdiscon); + pdiscon = NULL; + if (espconn ->proto.tcp->disconnect_callback != NULL) { + espconn ->proto.tcp->disconnect_callback(espconn); + } + } else { + espconn_printf("espconn_ssl_dissuccessful err\n"); + } +} + +/****************************************************************************** + * FunctionName : espconn_ssl_write + * Description : sent data for client or server + * Parameters : void *arg -- client or server to send + * uint8* psent -- Data to send + * uint16 length -- Length of data to send + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_ssl_sent(void *arg, uint8 *psent, uint16 length) +{ + espconn_msg *pssl_sent = arg; + struct tcp_pcb *pcb = NULL; + ssl_msg *pssl = NULL; + u16_t len = 0; + int res = 0; + + ssl_printf("espconn_ssl_sent pcb %p psent %p length %d\n", arg, psent, length); + + if (pssl_sent == NULL || psent == NULL || length == 0) { + return; + } + + pcb = pssl_sent->pcommon.pcb; + pssl = pssl_sent->pssl; + if (RT_MAX_PLAIN_LENGTH < length) { + len = RT_MAX_PLAIN_LENGTH; + } else { + len = length; + } + + if (pssl != NULL) { + if (pssl->ssl != NULL) { + pssl->ssl->SslClient_pcb = pcb; + res = ssl_write(pssl->ssl, psent, len); + pssl_sent->pcommon.ptrbuf = psent + len; + pssl_sent->pcommon.cntr = length - len; + } + } +} + +/****************************************************************************** + * FunctionName : espconn_sent_packet + * Description : sent data for client or server + * Parameters : void *arg -- client or server to send + * uint8* psent -- Data to send + * uint16 length -- Length of data to send + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_sent_packet(struct tcp_pcb *pcb, uint8 *psent, uint16 length) +{ + err_t err = 0; + u16_t len = 0; + if (pcb == NULL || psent == NULL || length == 0) { + return; + } + + if (tcp_sndbuf(pcb) < length) { + len = tcp_sndbuf(pcb); + } else { + len = length; + } + + if (len > (2 * pcb->mss)) { + len = 2 * pcb->mss; + } + + do { + err = tcp_write(pcb, psent, len, 0); + if (err == ERR_MEM) { + len /= 2; + } + } while (err == ERR_MEM && len > 1); + + if (err == ERR_OK) { + err = tcp_output(pcb); + } +} + +////////////////////////////////client function//////////////////////////////// +/****************************************************************************** + * FunctionName : espconn_ssl_cclose_cb + * Description : as + * Parameters : + * Returns : +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_ssl_cclose_cb(void *arg) +{ + static uint16 timecount = 0; + espconn_msg *pcclose_cb = arg; + + if (pcclose_cb == NULL) { + return; + } + + struct tcp_pcb *pcb = pcclose_cb->pcommon.pcb; + + ssl_printf("espconn_ssl_cclose_cb %d %d\n", pcb->state, pcb->nrtx); + + if (pcb->state == TIME_WAIT || pcb->state == CLOSED) { + pcclose_cb->pespconn ->state = ESPCONN_CLOSE; + /*remove the node from the client's active connection list*/ + espconn_list_delete(&plink_active, pcclose_cb); + espconn_ssl_dissuccessful((void *)pcclose_cb); + } else { + os_timer_arm(&pcclose_cb->pcommon.ptimer, TCP_FAST_INTERVAL, 0); + } +} + +/****************************************************************************** + * FunctionName : espconn_sslclient_close + * Description : The connection shall be actively closed. + * Parameters : pcb -- Additional argument to pass to the callback function + * pcb -- the pcb to close + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_ssl_cclose(void *arg, struct tcp_pcb *pcb) +{ + espconn_msg *pcclose = arg; + + os_timer_disarm(&pcclose->pcommon.ptimer); + os_timer_setfn(&pcclose->pcommon.ptimer, espconn_ssl_cclose_cb, pcclose); + os_timer_arm(&pcclose->pcommon.ptimer, TCP_FAST_INTERVAL, 0); + tcp_recv(pcb, NULL); + pcclose->pcommon.err = tcp_close(pcb); + ssl_printf("espconn_ssl_cclose %d\n", pcclose->pcommon.err); + + if (pcclose->pcommon.err != ERR_OK) { + /* closing failed, try again later */ + tcp_recv(pcb, espconn_ssl_crecv); + } else { + tcp_sent(pcb, NULL); + tcp_poll(pcb, NULL, 0); + } +} + +/****************************************************************************** + * FunctionName : espconn_sslclient_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 ICACHE_FLASH_ATTR +espconn_ssl_csent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + espconn_msg *psent = arg; + ssl_msg *pssl = psent->pssl; + psent->pcommon.pcb = pcb; + if (pssl->quiet == true) { + int pkt_size = pssl->ssl->bm_index + SSL_RECORD_SIZE; + u16_t max_len = 2 * pcb->mss; + pssl->pkt_length += len; + ssl_printf("espconn_ssl_csent %d %d %d\n", len, pssl->pkt_length, pkt_size); + if (pssl->pkt_length == pkt_size){ + pssl->ssl->bm_index = 0; + pssl->pkt_length = 0; + if (psent->pcommon.cntr == 0) { + psent->pespconn->state = ESPCONN_CONNECT; + if (psent->pespconn->sent_callback != NULL) { + psent->pespconn->sent_callback(psent->pespconn); + } + } else { + espconn_ssl_sent(psent, psent->pcommon.ptrbuf, psent->pcommon.cntr); + } + } else { + if (len == max_len){ + espconn_sent_packet(pcb, &pssl->ssl->bm_all_data[pssl->pkt_length], pkt_size - pssl->pkt_length); + } + } + + } else { + ssl_printf("espconn_ssl_csent %p %p %d\n", pcb, pssl->ssl->bm_all_data, len); + } + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_sslclient_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 ICACHE_FLASH_ATTR +espconn_ssl_crecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + u16_t ret = 0; + espconn_msg *precv = arg; + ssl_msg *pssl = precv->pssl; + ssl_printf("espconn_ssl_crecv %d %p %p\n", __LINE__, pssl->ssl, p); + + if (p != NULL) { + tcp_recved(pcb, p ->tot_len); + + if (pssl->ssl == NULL) { + pbuf_free(p); + } else { + pssl->ssl->ssl_pbuf = p; + + if (ssl_handshake_status(pssl->ssl) != SSL_OK) { + ret = ssl_read(pssl->ssl, NULL); + pbuf_free(p); + if (ret != SSL_OK){ + os_printf("client handshake failed\n"); + espconn_ssl_cclose(arg, pcb); + } + } + + if (ssl_handshake_status(pssl->ssl) == SSL_OK) { + if (!pssl->quiet) { + ssl_printf("client handshake need size %d\n", system_get_free_heap_size()); + const char *common_name = ssl_get_cert_dn(pssl->ssl, + SSL_X509_CERT_COMMON_NAME); + + if (common_name) { + ssl_printf("Common Name:\t\t\t%s\n", common_name); + } + + display_session_id(pssl->ssl); + display_cipher(pssl->ssl); + pssl->quiet = true; + os_printf("client handshake ok!\n"); + REG_CLR_BIT(0x3ff00014, BIT(0)); + os_update_cpu_frequency(80); + precv->pespconn->state = ESPCONN_CONNECT; + precv->pcommon.pcb = pcb; + pbuf_free(p); + + if (precv->pespconn->proto.tcp->connect_callback != NULL) { + precv->pespconn->proto.tcp->connect_callback(precv->pespconn); + } + } else { + uint8_t *read_buf = NULL; + ret = ssl_read(pssl->ssl, &read_buf); + precv->pespconn->state = ESPCONN_READ; + precv->pcommon.pcb = pcb; + pbuf_free(p); + + if (precv->pespconn->recv_callback != NULL && read_buf != NULL) { + precv->pespconn->recv_callback(precv->pespconn, read_buf, ret); + } + + precv->pespconn->state = ESPCONN_CONNECT; + } + } + } + + } + + if (err == ERR_OK && p == NULL) { + espconn_ssl_cclose(precv, pcb); + } + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_client_err + * 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 ICACHE_FLASH_ATTR +espconn_ssl_cerr(void *arg, err_t err) +{ + espconn_msg *pssl_cerr = arg; + struct tcp_pcb *pcb = NULL; + LWIP_UNUSED_ARG(err); + + if (pssl_cerr != NULL) { + os_timer_disarm(&pssl_cerr->pcommon.ptimer); + pcb = pssl_cerr->pcommon.pcb; + pssl_cerr->pespconn->state = ESPCONN_CLOSE; + espconn_printf("espconn_ssl_cerr %d %d %d\n", pcb->state, pcb->nrtx, err); + + /*remove the node from the client's active connection list*/ + espconn_list_delete(&plink_active, pssl_cerr); + + + if (err == ERR_ABRT) { + switch (pcb->state) { + case SYN_SENT: + if (pcb->nrtx == TCP_SYNMAXRTX) { + pssl_cerr->pcommon.err = ESPCONN_CONN; + } else { + pssl_cerr->pcommon.err = err; + } + + break; + + case ESTABLISHED: + if (pcb->nrtx == TCP_MAXRTX) { + pssl_cerr->pcommon.err = ESPCONN_TIMEOUT; + } else { + pssl_cerr->pcommon.err = err; + } + + break; + + case FIN_WAIT_1: + if (pcb->nrtx == TCP_MAXRTX) { + pssl_cerr->pcommon.err = ESPCONN_CLSD; + } else { + pssl_cerr->pcommon.err = err; + } + break; + case FIN_WAIT_2: + pssl_cerr->pcommon.err = ESPCONN_CLSD; + break; + case CLOSED: + pssl_cerr->pcommon.err = ESPCONN_CONN; + break; + default : + break; + } + } else { + pssl_cerr->pcommon.err = err; + } + + os_timer_setfn(&pssl_cerr->pcommon.ptimer, espconn_ssl_reconnect, pssl_cerr); + os_timer_arm(&pssl_cerr->pcommon.ptimer, 10, 0); + } +} + +#if 0 +/****************************************************************************** + * FunctionName : espconn_ssl_cpoll + * Description : The poll function is called every 3nd second. + * If there has been no data sent (which resets the retries) in 3 seconds, close. + * If the last portion of a file has not been sent in 3 seconds, close. + * + * 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 ICACHE_FLASH_ATTR +espconn_ssl_cpoll(void *arg, struct tcp_pcb *pcb) +{ + ssl_printf("espconn_ssl_cpoll %p %d\n", pcb, pcb->state); + struct espconn *espconn = arg; + + if (arg == NULL) { + tcp_abandon(pcb, 0); + tcp_poll(pcb, NULL, 0); + return ERR_ABRT; + } + + if (pcb ->state == ESTABLISHED) { + espconn->recv_check ++; + if (espconn ->recv_check == 0x05){ + //tcp_poll(pcb, espconn_ssl_cpoll, 0); + espconn->recv_check = 0; + espconn_ssl_cclose(arg, pcb); + } + } else { + //tcp_poll(pcb, espconn_ssl_cpoll, 0); + espconn_ssl_cclose(arg, pcb); + } + + return ERR_OK; +} +#endif +/****************************************************************************** + * FunctionName : espconn_sslclient_connect + * Description : A new incoming connection has been connected. + * Parameters : arg -- Additional argument to pass to the callback function + * tpcb -- The connection pcb which is connected + * err -- An unused error code, always ERR_OK currently + * Returns : connection result +*******************************************************************************/ +static err_t ICACHE_FLASH_ATTR +espconn_ssl_connect(void *arg, struct tcp_pcb *tpcb, err_t err) +{ + espconn_msg *pconnect = arg; + ssl_msg *pssl = NULL; + uint32_t options; + options = SSL_SERVER_VERIFY_LATER | SSL_DISPLAY_CERTS | SSL_NO_DEFAULT_KEY; + ssl_printf("espconn_ssl_connect %p %p %p %d\n", tpcb, arg, pespconn->psecure, system_get_free_heap_size()); + + //if (pespconn->psecure != NULL){ + // return ERR_ISCONN; + //} + + pconnect->pcommon.pcb = tpcb; + pssl = (ssl_msg *)os_zalloc(sizeof(ssl_msg)); + pconnect->pssl = pssl; + + if (pssl == NULL) { + return ERR_MEM; + } + + REG_SET_BIT(0x3ff00014, BIT(0)); + os_update_cpu_frequency(160); + os_printf("client handshake start.\n"); + pssl->quiet = false; + pssl->ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS); + + if (pssl->ssl_ctx == NULL) { + return ERR_MEM; + } + + ssl_printf("espconn_ssl_client ssl_ctx %p\n", pssl->ssl_ctx); + pssl->ssl = SSLClient_new(pssl->ssl_ctx, tpcb, NULL, 0); + + if (pssl->ssl == NULL) { + return ERR_MEM; + } + + tcp_arg(tpcb, arg); + tcp_sent(tpcb, espconn_ssl_csent); + tcp_recv(tpcb, espconn_ssl_crecv); + //tcp_poll(tpcb, espconn_ssl_cpoll, 6); + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_ssl_disconnect + * Description : A new incoming connection has been disconnected. + * Parameters : espconn -- the espconn used to disconnect with host + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_ssl_disconnect(espconn_msg *pdis) +{ + if (pdis != NULL) { + if (pdis->preverse == NULL) + espconn_ssl_cclose(pdis, pdis->pcommon.pcb); + else + espconn_ssl_sclose(pdis, pdis->pcommon.pcb); + } else { + ssl_printf("espconn_ssl_disconnect err.\n"); + } +} + +/****************************************************************************** + * FunctionName : espconn_ssl_client + * Description : Initialize the client: set up a connect PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build client + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_ssl_client(struct espconn *espconn) +{ + struct tcp_pcb *pcb; + struct ip_addr ipaddr; + espconn_msg *pclient = NULL; + pclient = plink_active; + while(pclient != NULL){ + if (pclient->pssl != NULL) + return ESPCONN_ISCONN; + + pclient = pclient->pnext; + } + pclient = (espconn_msg *)os_zalloc(sizeof(espconn_msg)); + if (pclient == NULL){ + return ESPCONN_MEM; + } + IP4_ADDR(&ipaddr, espconn->proto.tcp->remote_ip[0], + espconn->proto.tcp->remote_ip[1], + espconn->proto.tcp->remote_ip[2], + espconn->proto.tcp->remote_ip[3]); + pcb = tcp_new(); + if (pcb == NULL) { + espconn ->state = ESPCONN_NONE; + os_free(pclient); + pclient = NULL; + return ESPCONN_MEM; + } else { + /*insert the node to the active connection list*/ + espconn_list_creat(&plink_active, pclient); + tcp_arg(pcb, (void *)pclient); + tcp_err(pcb, espconn_ssl_cerr); + pclient->preverse = NULL; + pclient->pespconn = espconn; + pclient->pespconn->state = ESPCONN_WAIT; + pclient->pcommon.pcb = pcb; + tcp_bind(pcb, IP_ADDR_ANY, pclient->pespconn->proto.tcp->local_port); + pclient->pcommon.err = tcp_connect(pcb, &ipaddr, pclient->pespconn->proto.tcp->remote_port, espconn_ssl_connect); + return ESPCONN_OK; + } +} + +/////////////////////////////server's function///////////////////////////////// +/****************************************************************************** + * FunctionName : espconn_ssl_sclose_cb + * Description : as + * Parameters : + * Returns : +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_ssl_sclose_cb(void *arg) +{ + static uint16 timecount = 0; + espconn_msg *psclose_cb = arg; + + if (psclose_cb == NULL) { + return; + } + + struct tcp_pcb *pcb = psclose_cb->pcommon.pcb; + + ssl_printf("espconn_ssl_sclose_cb %d %d\n", pcb->state, pcb->nrtx); + + if (pcb->state == CLOSED || pcb->state == TIME_WAIT) { + psclose_cb ->pespconn ->state = ESPCONN_CLOSE; + psclose_cb->pespconn->link_cnt --; + /*remove the node from the server's active connection list*/ + espconn_list_delete(&plink_active, psclose_cb); + espconn_ssl_dissuccessful((void *)psclose_cb); + } else { + os_timer_arm(&psclose_cb->pcommon.ptimer, TCP_FAST_INTERVAL, 0); + } +} + +/****************************************************************************** + * FunctionName : espconn_sslclient_close + * Description : The connection shall be actively closed. + * Parameters : pcb -- Additional argument to pass to the callback function + * pcb -- the pcb to close + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_ssl_sclose(void *arg, struct tcp_pcb *pcb) +{ + espconn_msg *psclose = arg; + + os_timer_disarm(&psclose->pcommon.ptimer); + os_timer_setfn(&psclose->pcommon.ptimer, espconn_ssl_sclose_cb, psclose); + os_timer_arm(&psclose->pcommon.ptimer, TCP_FAST_INTERVAL, 0); + tcp_recv(pcb, NULL); + psclose->pcommon.err = tcp_close(pcb); + + if (psclose->pcommon.err != ERR_OK) { + /* closing failed, try again later */ + tcp_recv(pcb, espconn_ssl_srecv); + } else { + tcp_sent(pcb, NULL); + tcp_poll(pcb, NULL, 0); + } +} + + +/****************************************************************************** + * FunctionName : espconn_sslclient_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 ICACHE_FLASH_ATTR +espconn_ssl_ssent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + espconn_msg *psent = arg; + ssl_msg *pssl = psent->pssl; + psent->pcommon.pcb = pcb; + psent->pcommon.recv_check = 0; + if (ssl_handshake_status(pssl->ssl) == SSL_OK) { + if (!pssl->quiet) { + ssl_printf("espconn_ssl_ssent %p %d\n",pcb, system_get_free_heap_size()); + const char *common_name = ssl_get_cert_dn(pssl->ssl, SSL_X509_CERT_COMMON_NAME); + + if (common_name) { + ssl_printf("Common Name:\t\t\t%s\n", common_name); + } + + display_session_id(pssl->ssl); + display_cipher(pssl->ssl); + pssl->quiet = true; + os_printf("server handshake ok!\n"); + REG_CLR_BIT(0x3ff00014, BIT(0)); + os_update_cpu_frequency(80); + psent->pespconn->state = ESPCONN_CONNECT; + + if (psent->pespconn->proto.tcp->connect_callback != NULL) { + psent->pespconn->proto.tcp->connect_callback(psent->pespconn); + } + } else { + + int pkt_size = pssl->ssl->bm_index + SSL_RECORD_SIZE; + u16_t max_len = 2 * pcb->mss; + pssl->pkt_length += len; + ssl_printf("espconn_ssl_ssent %d %d %d\n", len, pssl->pkt_length, pkt_size); + if (pssl->pkt_length == pkt_size){ + pssl->ssl->bm_index = 0; + pssl->pkt_length = 0; + if (psent->pcommon.cntr == 0) { + psent->pespconn->state = ESPCONN_CONNECT; + if (psent->pespconn->sent_callback != NULL) { + psent->pespconn->sent_callback(psent->pespconn); + } + } else { + espconn_ssl_sent(psent, psent->pcommon.ptrbuf, psent->pcommon.cntr); + } + } else { + if (len == max_len){ + espconn_sent_packet(pcb, &pssl->ssl->bm_all_data[pssl->pkt_length], pkt_size - pssl->pkt_length); + } + } + } + } else { + ssl_printf("espconn_ssl_ssent %p %p %d\n",pcb, pssl->ssl->bm_all_data, len); + } + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_sslclient_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 ICACHE_FLASH_ATTR +espconn_ssl_srecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + u16_t ret = 0; + espconn_msg *precv = arg; + ssl_msg *pssl = precv->pssl; + ssl_printf("espconn_ssl_srecv %d %p %p\n", __LINE__, pcb, p); + + if (p != NULL) { + tcp_recved(pcb, p ->tot_len); + precv->pcommon.recv_check = 0; + if (pssl->ssl == NULL) { + pbuf_free(p); + } else { + pssl->ssl->ssl_pbuf = p; + + if (ssl_handshake_status(pssl->ssl) != SSL_OK) { + ret = ssl_read(pssl->ssl, NULL); + pbuf_free(p); + if (ret != SSL_OK){ + os_printf("server handshake failed.\n"); + espconn_ssl_sclose(arg, pcb); + } + } else { + uint8_t *read_buf = NULL; + ret = ssl_read(pssl->ssl, &read_buf); + precv->pespconn->state = ESPCONN_READ; + precv->pcommon.pcb = pcb; + pbuf_free(p); + + if (precv->pespconn->recv_callback != NULL && read_buf != NULL) { + precv->pespconn->recv_callback(precv->pespconn, read_buf, ret); + } + + precv->pespconn->state = ESPCONN_CONNECT; + } + + } + + } + + if (err == ERR_OK && p == NULL) { + espconn_ssl_sclose(precv, pcb); + } + + return ERR_OK; +} + + +/****************************************************************************** + * FunctionName : espconn_server_poll + * Description : The poll function is called every 3nd second. + * If there has been no data sent (which resets the retries) in 3 seconds, close. + * If the last portion of a file has not been sent in 3 seconds, close. + * + * 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 ICACHE_FLASH_ATTR +espconn_ssl_spoll(void *arg, struct tcp_pcb *pcb) +{ + ssl_printf("espconn_ssl_spoll %p %d\n", pcb, pcb->state); + espconn_msg *pspoll = arg; + if (arg == NULL) { + tcp_abandon(pcb, 0); + tcp_poll(pcb, NULL, 0); + return ERR_ABRT; + } + + if (pcb ->state == ESTABLISHED) { + pspoll ->pcommon.recv_check ++; + if (pspoll ->pcommon.recv_check == pspoll ->pcommon.timeout){ + tcp_poll(pcb, NULL, 0); + pspoll ->pcommon.recv_check = 0; + espconn_ssl_sclose(arg, pcb); + } + } else { + tcp_poll(pcb, NULL, 0); + espconn_ssl_sclose(arg, pcb); + } + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : esponn_server_err + * 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 ICACHE_FLASH_ATTR +espconn_ssl_serr(void *arg, err_t err) +{ + espconn_msg *pserr = arg; + struct tcp_pcb *pcb = NULL; + LWIP_UNUSED_ARG(err); + + if (pserr != NULL) { + os_timer_disarm(&pserr->pcommon.ptimer); + pcb = pserr->pcommon.pcb; + pserr->pespconn->state = ESPCONN_CLOSE; + + /*remove the node from the server's active connection list*/ + espconn_list_delete(&plink_active, pserr); + + if (err == ERR_ABRT) { + switch (pcb->state) { + case SYN_RCVD: + if (pcb->nrtx == TCP_SYNMAXRTX) { + pserr->pcommon.err = ESPCONN_CONN; + } else { + pserr->pcommon.err = err; + } + + break; + + case ESTABLISHED: + if (pcb->nrtx == TCP_MAXRTX) { + pserr->pcommon.err = ESPCONN_TIMEOUT; + } else { + pserr->pcommon.err = err; + } + + break; + + case CLOSE_WAIT: + if (pcb->nrtx == TCP_MAXRTX) { + pserr->pcommon.err = ESPCONN_CLSD; + } else { + pserr->pcommon.err = err; + } + break; + case LAST_ACK: + pserr->pcommon.err = ESPCONN_CLSD; + break; + case CLOSED: + pserr->pcommon.err = ESPCONN_CONN; + break; + default : + break; + } + } else { + pserr->pcommon.err = err; + } + + os_timer_setfn(&pserr->pcommon.ptimer, espconn_ssl_reconnect, pserr); + os_timer_arm(&pserr->pcommon.ptimer, 10, 0); + } +} + +/****************************************************************************** + * FunctionName : espconn_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 ICACHE_FLASH_ATTR +espconn_ssl_accept(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct espconn *espconn = arg; + ssl_msg *pssl = NULL; + espconn_msg *paccept = NULL; + remot_info *pinfo = NULL; + ssl_printf("espconn_ssl_accept %p %p %p %d\n", pcb, arg, espconn->psecure, system_get_free_heap_size()); + LWIP_UNUSED_ARG(err); + + paccept = (espconn_msg *)os_zalloc(sizeof(espconn_msg)); + tcp_arg(pcb, paccept); + tcp_err(pcb, espconn_ssl_serr); + if (paccept == NULL) + return ERR_MEM; + /*insert the node to the active connection list*/ + espconn_list_creat(&plink_active, paccept); + paccept->preverse = espconn; + paccept->pespconn = espconn; + + paccept->pcommon.timeout = 0x0a; + paccept->pcommon.pcb = pcb; + paccept->pcommon.remote_port = pcb->remote_port; + paccept->pcommon.remote_ip[0] = ip4_addr1_16(&pcb->remote_ip); + paccept->pcommon.remote_ip[1] = ip4_addr2_16(&pcb->remote_ip); + paccept->pcommon.remote_ip[2] = ip4_addr3_16(&pcb->remote_ip); + paccept->pcommon.remote_ip[3] = ip4_addr4_16(&pcb->remote_ip); + os_memcpy(espconn->proto.tcp->remote_ip, paccept->pcommon.remote_ip, 4); + espconn->proto.tcp->remote_port = pcb->remote_port; + + espconn_get_connection_info(espconn, &pinfo , ESPCONN_SSL); + if (espconn->link_cnt == 0x01) + return ERR_ISCONN; + + pssl = (ssl_msg *)os_zalloc(sizeof(ssl_msg)); + paccept->pssl = pssl; + if (pssl == NULL) { + return ERR_MEM; + } + + REG_SET_BIT(0x3ff00014, BIT(0)); + os_update_cpu_frequency(160); + os_printf("server handshake start.\n"); + pssl->quiet = false; + pssl->ssl_ctx = ssl_ctx_new(SSL_DISPLAY_CERTS, SSL_DEFAULT_SVR_SESS); + + if (pssl->ssl_ctx == NULL) { + ssl_printf("Error: Server context is invalid\n"); + return ERR_MEM; + } + + ssl_printf("Server context %p\n", pssl->ssl_ctx); + pssl->ssl = sslserver_new(pssl->ssl_ctx, pcb); + + if (pssl->ssl == NULL) { + ssl_printf("Error: Server ssl connection is invalid\n"); + return ERR_MEM; + + } + + tcp_sent(pcb, espconn_ssl_ssent); + tcp_recv(pcb, espconn_ssl_srecv); + tcp_poll(pcb, espconn_ssl_spoll, 2); + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_ssl_server + * Description : as + * Parameters : + * Returns : +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_ssl_server(struct espconn *espconn) +{ + struct tcp_pcb *pcb; + + pcb = tcp_new(); + if (pcb == NULL) { + espconn ->state = ESPCONN_NONE; + return ESPCONN_MEM; + } else { + tcp_bind(pcb, IP_ADDR_ANY, espconn->proto.tcp->local_port); + pcb = tcp_listen(pcb); + if (pcb != NULL) { + espconn ->state = ESPCONN_LISTEN; + tcp_arg(pcb, (void *)espconn); + tcp_accept(pcb, espconn_ssl_accept); + return ESPCONN_OK; + } else { + espconn ->state = ESPCONN_NONE; + return ESPCONN_MEM; + } + } +} + + diff --git a/app/ssl/crypto/Makefile b/app/ssl/crypto/Makefile new file mode 100644 index 00000000..c59000f9 --- /dev/null +++ b/app/ssl/crypto/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = libsslcrypto.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/ssl/crypto/ssl_aes.c b/app/ssl/crypto/ssl_aes.c new file mode 100644 index 00000000..efaa7a58 --- /dev/null +++ b/app/ssl/crypto/ssl_aes.c @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * AES implementation - this is a small code version. There are much faster + * versions around but they are much larger in size (i.e. they use large + * submix tables). + */ + +//#include +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_crypto.h" + +/* all commented out in skeleton mode */ +#ifndef CONFIG_SSL_SKELETON_MODE + +#define rot1(x) (((x) << 24) | ((x) >> 8)) +#define rot2(x) (((x) << 16) | ((x) >> 16)) +#define rot3(x) (((x) << 8) | ((x) >> 24)) + +/* + * This cute trick does 4 'mul by two' at once. Stolen from + * Dr B. R. Gladman but I'm sure the u-(u>>7) is + * a standard graphics trick + * The key to this is that we need to xor with 0x1b if the top bit is set. + * a 1xxx xxxx 0xxx 0xxx First we mask the 7bit, + * b 1000 0000 0000 0000 then we shift right by 7 putting the 7bit in 0bit, + * c 0000 0001 0000 0000 we then subtract (c) from (b) + * d 0111 1111 0000 0000 and now we and with our mask + * e 0001 1011 0000 0000 + */ +#define mt 0x80808080 +#define ml 0x7f7f7f7f +#define mh 0xfefefefe +#define mm 0x1b1b1b1b +#define mul2(x,t) ((t)=((x)&mt), \ + ((((x)+(x))&mh)^(((t)-((t)>>7))&mm))) + +#define inv_mix_col(x,f2,f4,f8,f9) (\ + (f2)=mul2(x,f2), \ + (f4)=mul2(f2,f4), \ + (f8)=mul2(f4,f8), \ + (f9)=(x)^(f8), \ + (f8)=((f2)^(f4)^(f8)), \ + (f2)^=(f9), \ + (f4)^=(f9), \ + (f8)^=rot3(f2), \ + (f8)^=rot2(f4), \ + (f8)^rot1(f9)) + +/* + * AES S-box + */ +static const uint8_t aes_sbox[256] = +{ + 0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5, + 0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76, + 0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0, + 0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0, + 0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC, + 0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15, + 0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A, + 0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75, + 0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0, + 0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84, + 0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B, + 0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF, + 0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85, + 0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8, + 0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5, + 0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2, + 0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17, + 0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73, + 0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88, + 0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB, + 0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C, + 0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79, + 0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9, + 0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08, + 0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6, + 0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A, + 0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E, + 0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E, + 0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94, + 0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF, + 0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68, + 0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16, +}; + +/* + * AES is-box + */ +static const uint8_t aes_isbox[256] = +{ + 0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38, + 0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb, + 0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87, + 0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb, + 0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d, + 0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e, + 0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2, + 0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25, + 0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16, + 0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92, + 0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda, + 0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84, + 0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a, + 0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06, + 0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02, + 0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b, + 0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea, + 0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73, + 0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85, + 0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e, + 0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89, + 0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b, + 0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20, + 0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4, + 0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31, + 0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f, + 0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d, + 0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef, + 0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0, + 0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61, + 0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26, + 0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d +}; + +static const unsigned char Rcon[30]= +{ + 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80, + 0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f, + 0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4, + 0xb3,0x7d,0xfa,0xef,0xc5,0x91, +}; + +/* ----- static functions ----- */ +static void AES_encrypt(const AES_CTX *ctx, uint32_t *data); +static void AES_decrypt(const AES_CTX *ctx, uint32_t *data); + +/* Perform doubling in Galois Field GF(2^8) using the irreducible polynomial + x^8+x^4+x^3+x+1 */ +static unsigned char ICACHE_FLASH_ATTR AES_xtime(uint32_t x) +{ + return (x&0x80) ? (x<<1)^0x1b : x<<1; +} + +/** + * Set up AES with the key/iv and cipher size. + */ +void ICACHE_FLASH_ATTR AES_set_key(AES_CTX *ctx, const uint8_t *key, + const uint8_t *iv, AES_MODE mode) +{ + int i, ii; + uint32_t *W, tmp, tmp2; + const unsigned char *ip; + int words; + + switch (mode) + { + case AES_MODE_128: + i = 10; + words = 4; + break; + + case AES_MODE_256: + i = 14; + words = 8; + break; + + default: /* fail silently */ + return; + } + + ctx->rounds = i; + ctx->key_size = words; + W = ctx->ks; + for (i = 0; i < words; i+=2) + { + W[i+0]= ((uint32_t)key[ 0]<<24)| + ((uint32_t)key[ 1]<<16)| + ((uint32_t)key[ 2]<< 8)| + ((uint32_t)key[ 3] ); + W[i+1]= ((uint32_t)key[ 4]<<24)| + ((uint32_t)key[ 5]<<16)| + ((uint32_t)key[ 6]<< 8)| + ((uint32_t)key[ 7] ); + key += 8; + } + + ip = Rcon; + ii = 4 * (ctx->rounds+1); + for (i = words; i> 8)&0xff]<<16; + tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<24; + tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]; + tmp=tmp2^(((unsigned int)*ip)<<24); + ip++; + } + + if ((words == 8) && ((i % words) == 4)) + { + tmp2 =(uint32_t)aes_sbox[(tmp )&0xff] ; + tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<< 8; + tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<16; + tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]<<24; + tmp=tmp2; + } + + W[i]=W[i-words]^tmp; + } + + /* copy the iv across */ + os_memcpy(ctx->iv, iv, 16); +} + +/** + * Change a key for decryption. + */ +void ICACHE_FLASH_ATTR AES_convert_key(AES_CTX *ctx) +{ + int i; + uint32_t *k,w,t1,t2,t3,t4; + + k = ctx->ks; + k += 4; + + for (i= ctx->rounds*4; i > 4; i--) + { + w= *k; + w = inv_mix_col(w,t1,t2,t3,t4); + *k++ =w; + } +} + +/** + * Encrypt a byte sequence (with a block size 16) using the AES cipher. + */ +void ICACHE_FLASH_ATTR AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ + int i; + uint32_t tin[4], tout[4], iv[4]; + + os_memcpy(iv, ctx->iv, AES_IV_SIZE); + for (i = 0; i < 4; i++) + tout[i] = ntohl(iv[i]); + + for (length -= AES_BLOCKSIZE; length >= 0; length -= AES_BLOCKSIZE) + { + uint32_t msg_32[4]; + uint32_t out_32[4]; + os_memcpy(msg_32, msg, AES_BLOCKSIZE); + msg += AES_BLOCKSIZE; + + for (i = 0; i < 4; i++) + tin[i] = ntohl(msg_32[i])^tout[i]; + + AES_encrypt(ctx, tin); + + for (i = 0; i < 4; i++) + { + tout[i] = tin[i]; + out_32[i] = htonl(tout[i]); + } + + os_memcpy(out, out_32, AES_BLOCKSIZE); + out += AES_BLOCKSIZE; + } + + for (i = 0; i < 4; i++) + iv[i] = htonl(tout[i]); + os_memcpy(ctx->iv, iv, AES_IV_SIZE); +} + +/** + * Decrypt a byte sequence (with a block size 16) using the AES cipher. + */ +void ICACHE_FLASH_ATTR AES_cbc_decrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ + int i; + uint32_t tin[4], xor[4], tout[4], data[4], iv[4]; + + os_memcpy(iv, ctx->iv, AES_IV_SIZE); + for (i = 0; i < 4; i++) + xor[i] = ntohl(iv[i]); + + for (length -= 16; length >= 0; length -= 16) + { + uint32_t msg_32[4]; + uint32_t out_32[4]; + os_memcpy(msg_32, msg, AES_BLOCKSIZE); + msg += AES_BLOCKSIZE; + + for (i = 0; i < 4; i++) + { + tin[i] = ntohl(msg_32[i]); + data[i] = tin[i]; + } + + AES_decrypt(ctx, data); + + for (i = 0; i < 4; i++) + { + tout[i] = data[i]^xor[i]; + xor[i] = tin[i]; + out_32[i] = htonl(tout[i]); + } + + os_memcpy(out, out_32, AES_BLOCKSIZE); + out += AES_BLOCKSIZE; + } + + for (i = 0; i < 4; i++) + iv[i] = htonl(xor[i]); + os_memcpy(ctx->iv, iv, AES_IV_SIZE); +} + +/** + * Encrypt a single block (16 bytes) of data + */ +static void ICACHE_FLASH_ATTR AES_encrypt(const AES_CTX *ctx, uint32_t *data) +{ + /* To make this code smaller, generate the sbox entries on the fly. + * This will have a really heavy effect upon performance. + */ + uint32_t tmp[4]; + uint32_t tmp1, old_a0, a0, a1, a2, a3, row; + int curr_rnd; + int rounds = ctx->rounds; + const uint32_t *k = ctx->ks; + + /* Pre-round key addition */ + for (row = 0; row < 4; row++) + data[row] ^= *(k++); + + /* Encrypt one block. */ + for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) + { + /* Perform ByteSub and ShiftRow operations together */ + for (row = 0; row < 4; row++) + { + a0 = (uint32_t)aes_sbox[(data[row%4]>>24)&0xFF]; + a1 = (uint32_t)aes_sbox[(data[(row+1)%4]>>16)&0xFF]; + a2 = (uint32_t)aes_sbox[(data[(row+2)%4]>>8)&0xFF]; + a3 = (uint32_t)aes_sbox[(data[(row+3)%4])&0xFF]; + + /* Perform MixColumn iff not last round */ + if (curr_rnd < (rounds - 1)) + { + tmp1 = a0 ^ a1 ^ a2 ^ a3; + old_a0 = a0; + a0 ^= tmp1 ^ AES_xtime(a0 ^ a1); + a1 ^= tmp1 ^ AES_xtime(a1 ^ a2); + a2 ^= tmp1 ^ AES_xtime(a2 ^ a3); + a3 ^= tmp1 ^ AES_xtime(a3 ^ old_a0); + } + + tmp[row] = ((a0 << 24) | (a1 << 16) | (a2 << 8) | a3); + } + + /* KeyAddition - note that it is vital that this loop is separate from + the MixColumn operation, which must be atomic...*/ + for (row = 0; row < 4; row++) + data[row] = tmp[row] ^ *(k++); + } +} + +/** + * Decrypt a single block (16 bytes) of data + */ +static void ICACHE_FLASH_ATTR AES_decrypt(const AES_CTX *ctx, uint32_t *data) +{ + uint32_t tmp[4]; + uint32_t xt0,xt1,xt2,xt3,xt4,xt5,xt6; + uint32_t a0, a1, a2, a3, row; + int curr_rnd; + int rounds = ctx->rounds; + const uint32_t *k = ctx->ks + ((rounds+1)*4); + + /* pre-round key addition */ + for (row=4; row > 0;row--) + data[row-1] ^= *(--k); + + /* Decrypt one block */ + for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) + { + /* Perform ByteSub and ShiftRow operations together */ + for (row = 4; row > 0; row--) + { + a0 = aes_isbox[(data[(row+3)%4]>>24)&0xFF]; + a1 = aes_isbox[(data[(row+2)%4]>>16)&0xFF]; + a2 = aes_isbox[(data[(row+1)%4]>>8)&0xFF]; + a3 = aes_isbox[(data[row%4])&0xFF]; + + /* Perform MixColumn iff not last round */ + if (curr_rnd<(rounds-1)) + { + /* The MDS cofefficients (0x09, 0x0B, 0x0D, 0x0E) + are quite large compared to encryption; this + operation slows decryption down noticeably. */ + xt0 = AES_xtime(a0^a1); + xt1 = AES_xtime(a1^a2); + xt2 = AES_xtime(a2^a3); + xt3 = AES_xtime(a3^a0); + xt4 = AES_xtime(xt0^xt1); + xt5 = AES_xtime(xt1^xt2); + xt6 = AES_xtime(xt4^xt5); + + xt0 ^= a1^a2^a3^xt4^xt6; + xt1 ^= a0^a2^a3^xt5^xt6; + xt2 ^= a0^a1^a3^xt4^xt6; + xt3 ^= a0^a1^a2^xt5^xt6; + tmp[row-1] = ((xt0<<24)|(xt1<<16)|(xt2<<8)|xt3); + } + else + tmp[row-1] = ((a0<<24)|(a1<<16)|(a2<<8)|a3); + } + + for (row = 4; row > 0; row--) + data[row-1] = tmp[row-1] ^ *(--k); + } +} + +#endif diff --git a/app/ssl/crypto/ssl_bigint.c b/app/ssl/crypto/ssl_bigint.c new file mode 100644 index 00000000..2db22d9f --- /dev/null +++ b/app/ssl/crypto/ssl_bigint.c @@ -0,0 +1,1514 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @defgroup bigint_api Big Integer API + * @brief The bigint implementation as used by the axTLS project. + * + * The bigint library is for RSA encryption/decryption as well as signing. + * This code tries to minimise use of malloc/free by maintaining a small + * cache. A bigint context may maintain state by being made "permanent". + * It be be later released with a bi_depermanent() and bi_free() call. + * + * It supports the following reduction techniques: + * - Classical + * - Barrett + * - Montgomery + * + * It also implements the following: + * - Karatsuba multiplication + * - Squaring + * - Sliding window exponentiation + * - Chinese Remainder Theorem (implemented in rsa.c). + * + * All the algorithms used are pretty standard, and designed for different + * data bus sizes. Negative numbers are not dealt with at all, so a subtraction + * may need to be tested for negativity. + * + * This library steals some ideas from Jef Poskanzer + * + * and GMP . It gets most of its implementation + * detail from "The Handbook of Applied Cryptography" + * + * @{ + */ + +//#include +//#include +//#include +//#include +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_bigint.h" +//#include "os.h" +#include "lwip/mem.h" + +#define V1 v->comps[v->size-1] /**< v1 for division */ +#define V2 v->comps[v->size-2] /**< v2 for division */ +#define U(j) tmp_u->comps[tmp_u->size-j-1] /**< uj for division */ +#define Q(j) quotient->comps[quotient->size-j-1] /**< qj for division */ + +static bigint *bi_int_multiply(BI_CTX *ctx, bigint *bi, comp i); +static bigint *bi_int_divide(BI_CTX *ctx, bigint *biR, comp denom); +static bigint *alloc(BI_CTX *ctx, int size); +static bigint *trim(bigint *bi); +static void more_comps(bigint *bi, int n); +#if defined(CONFIG_BIGINT_KARATSUBA) || defined(CONFIG_BIGINT_BARRETT) || \ + defined(CONFIG_BIGINT_MONTGOMERY) +static bigint *comp_right_shift(bigint *biR, int num_shifts); +static bigint *comp_left_shift(bigint *biR, int num_shifts); +#endif + +#ifdef CONFIG_BIGINT_CHECK_ON +static void check(const bigint *bi); +#else +#define check(A) /**< disappears in normal production mode */ +#endif + + +/** + * @brief Start a new bigint context. + * @return A bigint context. + */ +BI_CTX * ICACHE_FLASH_ATTR bi_initialize(void) +{ + /* calloc() sets everything to zero */ + BI_CTX *ctx = (BI_CTX *)os_zalloc(sizeof(BI_CTX)); + + /* the radix */ + ctx->bi_radix = alloc(ctx, 2); + ctx->bi_radix->comps[0] = 0; + ctx->bi_radix->comps[1] = 1; + bi_permanent(ctx->bi_radix); + return ctx; +} + +/** + * @brief Close the bigint context and free any resources. + * + * Free up any used memory - a check is done if all objects were not + * properly freed. + * @param ctx [in] The bigint session context. + */ +void ICACHE_FLASH_ATTR bi_terminate(BI_CTX *ctx) +{ + bi_depermanent(ctx->bi_radix); + bi_free(ctx, ctx->bi_radix); + + if (ctx->active_count != 0) + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("bi_terminate: there were %d un-freed bigints\n", + ctx->active_count); +#endif + return; /* wujg : org ---> abort(); */ + } + + bi_clear_cache(ctx); + os_free(ctx); +} + +/** + *@brief Clear the memory cache. + */ +void ICACHE_FLASH_ATTR bi_clear_cache(BI_CTX *ctx) +{ + bigint *p, *pn; + + if (ctx->free_list == NULL) + return; + + for (p = ctx->free_list; p != NULL; p = pn) + { + pn = p->next; + os_free(p->comps); + os_free(p); + } + + ctx->free_count = 0; + ctx->free_list = NULL; +} + +/** + * @brief Increment the number of references to this object. + * It does not do a full copy. + * @param bi [in] The bigint to copy. + * @return A reference to the same bigint. + */ +bigint * ICACHE_FLASH_ATTR bi_copy(bigint *bi) +{ + check(bi); + if (bi->refs != PERMANENT) + bi->refs++; + return bi; +} + +/** + * @brief Simply make a bigint object "unfreeable" if bi_free() is called on it. + * + * For this object to be freed, bi_depermanent() must be called. + * @param bi [in] The bigint to be made permanent. + */ +void ICACHE_FLASH_ATTR bi_permanent(bigint *bi) +{ + check(bi); + if (bi->refs != 1) + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("bi_permanent: refs was not 1\n"); +#endif + return; /* wujg : org ----> abort(); */ + } + + bi->refs = PERMANENT; +} + +/** + * @brief Take a permanent object and make it eligible for freedom. + * @param bi [in] The bigint to be made back to temporary. + */ +void ICACHE_FLASH_ATTR bi_depermanent(bigint *bi) +{ + check(bi); + if (bi->refs != PERMANENT) + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("bi_depermanent: bigint was not permanent\n"); +#endif + return; /* wujg : org ----> abort(); */ + } + + bi->refs = 1; +} + +/** + * @brief Free a bigint object so it can be used again. + * + * The memory itself it not actually freed, just tagged as being available + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint to be freed. + */ +void ICACHE_FLASH_ATTR bi_free(BI_CTX *ctx, bigint *bi) +{ + check(bi); + if (bi->refs == PERMANENT) + { + return; + } + + if (--bi->refs > 0) + { + return; + } + + bi->next = ctx->free_list; + ctx->free_list = bi; + ctx->free_count++; + + if (--ctx->active_count < 0) + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("bi_free: active_count went negative " + "- double-freed bigint?\n"); +#endif + return; /* wujg : org ----> abort(); */ + } +} + +/** + * @brief Convert an (unsigned) integer into a bigint. + * @param ctx [in] The bigint session context. + * @param i [in] The (unsigned) integer to be converted. + * + */ +bigint * ICACHE_FLASH_ATTR int_to_bi(BI_CTX *ctx, comp i) +{ + bigint *biR = alloc(ctx, 1); + biR->comps[0] = i; + return biR; +} + +/** + * @brief Do a full copy of the bigint object. + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint object to be copied. + */ +bigint * ICACHE_FLASH_ATTR bi_clone(BI_CTX *ctx, const bigint *bi) +{ + bigint *biR = alloc(ctx, bi->size); + check(bi); + os_memcpy(biR->comps, bi->comps, bi->size*COMP_BYTE_SIZE); + return biR; +} + +/** + * @brief Perform an addition operation between two bigints. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @return The result of the addition. + */ +bigint * ICACHE_FLASH_ATTR bi_add(BI_CTX *ctx, bigint *bia, bigint *bib) +{ + int n; + comp carry = 0; + comp *pa, *pb; + + check(bia); + check(bib); + + n = max(bia->size, bib->size); + more_comps(bia, n+1); + more_comps(bib, n); + pa = bia->comps; + pb = bib->comps; + + do + { + comp sl, rl, cy1; + sl = *pa + *pb++; + rl = sl + carry; + cy1 = sl < *pa; + carry = cy1 | (rl < sl); + *pa++ = rl; + } while (--n != 0); + + *pa = carry; /* do overflow */ + bi_free(ctx, bib); + return trim(bia); +} + +/** + * @brief Perform a subtraction operation between two bigints. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @param is_negative [out] If defined, indicates that the result was negative. + * is_negative may be null. + * @return The result of the subtraction. The result is always positive. + */ +bigint * ICACHE_FLASH_ATTR bi_subtract(BI_CTX *ctx, + bigint *bia, bigint *bib, int *is_negative) +{ + int n = bia->size; + comp *pa, *pb, carry = 0; + + check(bia); + check(bib); + + more_comps(bib, n); + pa = bia->comps; + pb = bib->comps; + + do + { + comp sl, rl, cy1; + sl = *pa - *pb++; + rl = sl - carry; + cy1 = sl > *pa; + carry = cy1 | (rl > sl); + *pa++ = rl; + } while (--n != 0); + + if (is_negative) /* indicate a negative result */ + { + *is_negative = carry; + } + + bi_free(ctx, trim(bib)); /* put bib back to the way it was */ + return trim(bia); +} + +/** + * Perform a multiply between a bigint an an (unsigned) integer + */ +static bigint * ICACHE_FLASH_ATTR bi_int_multiply(BI_CTX *ctx, bigint *bia, comp b) +{ + int j = 0, n = bia->size; + bigint *biR = alloc(ctx, n + 1); + comp carry = 0; + comp *r = biR->comps; + comp *a = bia->comps; + + check(bia); + + /* clear things to start with */ + os_memset(r, 0, ((n+1)*COMP_BYTE_SIZE)); + + do + { + long_comp tmp = *r + (long_comp)a[j]*b + carry; + *r++ = (comp)tmp; /* downsize */ + carry = (comp)(tmp >> COMP_BIT_SIZE); + } while (++j < n); + + *r = carry; + bi_free(ctx, bia); + return trim(biR); +} + +/** + * @brief Does both division and modulo calculations. + * + * Used extensively when doing classical reduction. + * @param ctx [in] The bigint session context. + * @param u [in] A bigint which is the numerator. + * @param v [in] Either the denominator or the modulus depending on the mode. + * @param is_mod [n] Determines if this is a normal division (0) or a reduction + * (1). + * @return The result of the division/reduction. + */ +bigint * ICACHE_FLASH_ATTR bi_divide(BI_CTX *ctx, bigint *u, bigint *v, int is_mod) +{ + int n = v->size, m = u->size-n; + int j = 0, orig_u_size = u->size; + uint8_t mod_offset = ctx->mod_offset; + comp d; + bigint *quotient, *tmp_u; + comp q_dash; + + check(u); + check(v); + + /* if doing reduction and we are < mod, then return mod */ + if (is_mod && bi_compare(v, u) > 0) + { + bi_free(ctx, v); + return u; + } + + quotient = alloc(ctx, m+1); + tmp_u = alloc(ctx, n+1); + v = trim(v); /* make sure we have no leading 0's */ + d = (comp)((long_comp)COMP_RADIX/(V1+1)); + + /* clear things to start with */ + os_memset(quotient->comps, 0, ((quotient->size)*COMP_BYTE_SIZE)); + + /* normalise */ + if (d > 1) + { + u = bi_int_multiply(ctx, u, d); + + if (is_mod) + { + v = ctx->bi_normalised_mod[mod_offset]; + } + else + { + v = bi_int_multiply(ctx, v, d); + } + } + + if (orig_u_size == u->size) /* new digit position u0 */ + { + more_comps(u, orig_u_size + 1); + } + + do + { + /* get a temporary short version of u */ + os_memcpy(tmp_u->comps, &u->comps[u->size-n-1-j], (n+1)*COMP_BYTE_SIZE); + + /* calculate q' */ + if (U(0) == V1) + { + q_dash = COMP_RADIX-1; + } + else + { + q_dash = (comp)(((long_comp)U(0)*COMP_RADIX + U(1))/V1); + + if (v->size > 1 && V2) + { + /* we are implementing the following: + if (V2*q_dash > (((U(0)*COMP_RADIX + U(1) - + q_dash*V1)*COMP_RADIX) + U(2))) ... */ + comp inner = (comp)((long_comp)COMP_RADIX*U(0) + U(1) - + (long_comp)q_dash*V1); + if ((long_comp)V2*q_dash > (long_comp)inner*COMP_RADIX + U(2)) + { + q_dash--; + } + } + } + + /* multiply and subtract */ + if (q_dash) + { + int is_negative; + tmp_u = bi_subtract(ctx, tmp_u, + bi_int_multiply(ctx, bi_copy(v), q_dash), &is_negative); + more_comps(tmp_u, n+1); + + Q(j) = q_dash; + + /* add back */ + if (is_negative) + { + Q(j)--; + tmp_u = bi_add(ctx, tmp_u, bi_copy(v)); + + /* lop off the carry */ + tmp_u->size--; + v->size--; + } + } + else + { + Q(j) = 0; + } + + /* copy back to u */ + os_memcpy(&u->comps[u->size-n-1-j], tmp_u->comps, (n+1)*COMP_BYTE_SIZE); + } while (++j <= m); + + bi_free(ctx, tmp_u); + bi_free(ctx, v); + + if (is_mod) /* get the remainder */ + { + bi_free(ctx, quotient); + return bi_int_divide(ctx, trim(u), d); + } + else /* get the quotient */ + { + bi_free(ctx, u); + return trim(quotient); + } +} + +/* + * Perform an integer divide on a bigint. + */ +static bigint * ICACHE_FLASH_ATTR bi_int_divide(BI_CTX *ctx, bigint *biR, comp denom) +{ + int i = biR->size - 1; + long_comp r = 0; + + check(biR); + + do + { + r = (r<comps[i]; + biR->comps[i] = (comp)(r / denom); + r %= denom; + } while (--i >= 0); + + return trim(biR); +} + +#ifdef CONFIG_BIGINT_MONTGOMERY +/** + * There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1, + * where B^-1(B-1) mod N=1. Actually, only the least significant part of + * N' is needed, hence the definition N0'=N' mod b. We reproduce below the + * simple algorithm from an article by Dusse and Kaliski to efficiently + * find N0' from N0 and b */ +static comp ICACHE_FLASH_ATTR modular_inverse(bigint *bim) +{ + int i; + comp t = 1; + comp two_2_i_minus_1 = 2; /* 2^(i-1) */ + long_comp two_2_i = 4; /* 2^i */ + comp N = bim->comps[0]; + + for (i = 2; i <= COMP_BIT_SIZE; i++) + { + if ((long_comp)N*t%two_2_i >= two_2_i_minus_1) + { + t += two_2_i_minus_1; + } + + two_2_i_minus_1 <<= 1; + two_2_i <<= 1; + } + + return (comp)(COMP_RADIX-t); +} +#endif + +#if defined(CONFIG_BIGINT_KARATSUBA) || defined(CONFIG_BIGINT_BARRETT) || \ + defined(CONFIG_BIGINT_MONTGOMERY) +/** + * Take each component and shift down (in terms of components) + */ +static bigint * ICACHE_FLASH_ATTR comp_right_shift(bigint *biR, int num_shifts) +{ + int i = biR->size-num_shifts; + comp *x = biR->comps; + comp *y = &biR->comps[num_shifts]; + + check(biR); + + if (i <= 0) /* have we completely right shifted? */ + { + biR->comps[0] = 0; /* return 0 */ + biR->size = 1; + return biR; + } + + do + { + *x++ = *y++; + } while (--i > 0); + + biR->size -= num_shifts; + return biR; +} + +/** + * Take each component and shift it up (in terms of components) + */ +static bigint * ICACHE_FLASH_ATTR comp_left_shift(bigint *biR, int num_shifts) +{ + int i = biR->size-1; + comp *x, *y; + + check(biR); + + if (num_shifts <= 0) + { + return biR; + } + + more_comps(biR, biR->size + num_shifts); + + x = &biR->comps[i+num_shifts]; + y = &biR->comps[i]; + + do + { + *x-- = *y--; + } while (i--); + + os_memset(biR->comps, 0, num_shifts*COMP_BYTE_SIZE); /* zero LS comps */ + return biR; +} +#endif + +/** + * @brief Allow a binary sequence to be imported as a bigint. + * @param ctx [in] The bigint session context. + * @param data [in] The data to be converted. + * @param size [in] The number of bytes of data. + * @return A bigint representing this data. + */ +bigint * ICACHE_FLASH_ATTR bi_import(BI_CTX *ctx, const uint8_t *data, int size) +{ + bigint *biR = alloc(ctx, (size+COMP_BYTE_SIZE-1)/COMP_BYTE_SIZE); + int i, j = 0, offset = 0; + + os_memset(biR->comps, 0, biR->size*COMP_BYTE_SIZE); + + for (i = size-1; i >= 0; i--) + { + biR->comps[offset] += data[i] << (j*8); + + if (++j == COMP_BYTE_SIZE) + { + j = 0; + offset ++; + } + } + + return trim(biR); +} + +#ifdef CONFIG_SSL_FULL_MODE +/** + * @brief The testharness uses this code to import text hex-streams and + * convert them into bigints. + * @param ctx [in] The bigint session context. + * @param data [in] A string consisting of hex characters. The characters must + * be in upper case. + * @return A bigint representing this data. + */ +bigint * ICACHE_FLASH_ATTR bi_str_import(BI_CTX *ctx, const char *data) +{ + int size = os_strlen(data); + bigint *biR = alloc(ctx, (size+COMP_NUM_NIBBLES-1)/COMP_NUM_NIBBLES); + int i, j = 0, offset = 0; + os_memset(biR->comps, 0, biR->size*COMP_BYTE_SIZE); + + for (i = size-1; i >= 0; i--) + { + int num = (data[i] <= '9') ? (data[i] - '0') : (data[i] - 'A' + 10); + biR->comps[offset] += num << (j*4); + + if (++j == COMP_NUM_NIBBLES) + { + j = 0; + offset ++; + } + } + + return biR; +} + +void ICACHE_FLASH_ATTR bi_print(const char *label, bigint *x) +{ + int i, j; + + if (x == NULL) + { + ssl_printf("%s: (null)\n", label); + return; + } + + ssl_printf("%s: (size %d)\n", label, x->size); + for (i = x->size-1; i >= 0; i--) + { + for (j = COMP_NUM_NIBBLES-1; j >= 0; j--) + { + comp mask = 0x0f << (j*4); + comp num = (x->comps[i] & mask) >> (j*4); + os_putc((num <= 9) ? (num + '0') : (num + 'A' - 10)); + } + } + + ssl_printf("\n"); +} +#endif + +/** + * @brief Take a bigint and convert it into a byte sequence. + * + * This is useful after a decrypt operation. + * @param ctx [in] The bigint session context. + * @param x [in] The bigint to be converted. + * @param data [out] The converted data as a byte stream. + * @param size [in] The maximum size of the byte stream. Unused bytes will be + * zeroed. + */ +void ICACHE_FLASH_ATTR bi_export(BI_CTX *ctx, bigint *x, uint8_t *data, int size) +{ + int i, j, k = size-1; + + check(x); + os_memset(data, 0, size); /* ensure all leading 0's are cleared */ + + for (i = 0; i < x->size; i++) + { + for (j = 0; j < COMP_BYTE_SIZE; j++) + { + comp mask = 0xff << (j*8); + int num = (x->comps[i] & mask) >> (j*8); + data[k--] = num; + + if (k < 0) + { + goto buf_done; + } + } + } +buf_done: + + bi_free(ctx, x); +} + +/** + * @brief Pre-calculate some of the expensive steps in reduction. + * + * This function should only be called once (normally when a session starts). + * When the session is over, bi_free_mod() should be called. bi_mod_power() + * relies on this function being called. + * @param ctx [in] The bigint session context. + * @param bim [in] The bigint modulus that will be used. + * @param mod_offset [in] There are three moduluii that can be stored - the + * standard modulus, and its two primes p and q. This offset refers to which + * modulus we are referring to. + * @see bi_free_mod(), bi_mod_power(). + */ +void ICACHE_FLASH_ATTR bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset) +{ + int k = bim->size; + comp d = (comp)((long_comp)COMP_RADIX/(bim->comps[k-1]+1)); +#ifdef CONFIG_BIGINT_MONTGOMERY + bigint *R, *R2; +#endif + + ctx->bi_mod[mod_offset] = bim; + bi_permanent(ctx->bi_mod[mod_offset]); + ctx->bi_normalised_mod[mod_offset] = bi_int_multiply(ctx, bim, d); + bi_permanent(ctx->bi_normalised_mod[mod_offset]); + +#if defined(CONFIG_BIGINT_MONTGOMERY) + /* set montgomery variables */ + R = comp_left_shift(bi_clone(ctx, ctx->bi_radix), k-1); /* R */ + R2 = comp_left_shift(bi_clone(ctx, ctx->bi_radix), k*2-1); /* R^2 */ + ctx->bi_RR_mod_m[mod_offset] = bi_mod(ctx, R2); /* R^2 mod m */ + ctx->bi_R_mod_m[mod_offset] = bi_mod(ctx, R); /* R mod m */ + + bi_permanent(ctx->bi_RR_mod_m[mod_offset]); + bi_permanent(ctx->bi_R_mod_m[mod_offset]); + + ctx->N0_dash[mod_offset] = modular_inverse(ctx->bi_mod[mod_offset]); + +#elif defined (CONFIG_BIGINT_BARRETT) + ctx->bi_mu[mod_offset] = + bi_divide(ctx, comp_left_shift( + bi_clone(ctx, ctx->bi_radix), k*2-1), ctx->bi_mod[mod_offset], 0); + bi_permanent(ctx->bi_mu[mod_offset]); +#endif +} + +/** + * @brief Used when cleaning various bigints at the end of a session. + * @param ctx [in] The bigint session context. + * @param mod_offset [in] The offset to use. + * @see bi_set_mod(). + */ +void ICACHE_FLASH_ATTR bi_free_mod(BI_CTX *ctx, int mod_offset) +{ + bi_depermanent(ctx->bi_mod[mod_offset]); + bi_free(ctx, ctx->bi_mod[mod_offset]); +#if defined (CONFIG_BIGINT_MONTGOMERY) + bi_depermanent(ctx->bi_RR_mod_m[mod_offset]); + bi_depermanent(ctx->bi_R_mod_m[mod_offset]); + bi_free(ctx, ctx->bi_RR_mod_m[mod_offset]); + bi_free(ctx, ctx->bi_R_mod_m[mod_offset]); +#elif defined(CONFIG_BIGINT_BARRETT) + bi_depermanent(ctx->bi_mu[mod_offset]); + bi_free(ctx, ctx->bi_mu[mod_offset]); +#endif + bi_depermanent(ctx->bi_normalised_mod[mod_offset]); + bi_free(ctx, ctx->bi_normalised_mod[mod_offset]); +} + +/** + * Perform a standard multiplication between two bigints. + * + * Barrett reduction has no need for some parts of the product, so ignore bits + * of the multiply. This routine gives Barrett its big performance + * improvements over Classical/Montgomery reduction methods. + */ +static bigint * ICACHE_FLASH_ATTR regular_multiply(BI_CTX *ctx, bigint *bia, bigint *bib, + int inner_partial, int outer_partial) +{ + int i = 0, j; + int n = bia->size; + int t = bib->size; + bigint *biR = alloc(ctx, n + t); + comp *sr = biR->comps; + comp *sa = bia->comps; + comp *sb = bib->comps; + + check(bia); + check(bib); + + /* clear things to start with */ + os_memset(biR->comps, 0, ((n+t)*COMP_BYTE_SIZE)); + + do + { + long_comp tmp; + comp carry = 0; + int r_index = i; + j = 0; + + if (outer_partial && outer_partial-i > 0 && outer_partial < n) + { + r_index = outer_partial-1; + j = outer_partial-i-1; + } + + do + { + if (inner_partial && r_index >= inner_partial) + { + break; + } + + tmp = sr[r_index] + ((long_comp)sa[j])*sb[i] + carry; + sr[r_index++] = (comp)tmp; /* downsize */ + carry = tmp >> COMP_BIT_SIZE; + } while (++j < n); + + sr[r_index] = carry; + } while (++i < t); + + bi_free(ctx, bia); + bi_free(ctx, bib); + return trim(biR); +} + +#ifdef CONFIG_BIGINT_KARATSUBA +/* + * Karatsuba improves on regular multiplication due to only 3 multiplications + * being done instead of 4. The additional additions/subtractions are O(N) + * rather than O(N^2) and so for big numbers it saves on a few operations + */ +static bigint * ICACHE_FLASH_ATTR karatsuba(BI_CTX *ctx, bigint *bia, bigint *bib, int is_square) +{ + bigint *x0, *x1; + bigint *p0, *p1, *p2; + int m; + + if (is_square) + { + m = (bia->size + 1)/2; + } + else + { + m = (max(bia->size, bib->size) + 1)/2; + } + + x0 = bi_clone(ctx, bia); + x0->size = m; + x1 = bi_clone(ctx, bia); + comp_right_shift(x1, m); + bi_free(ctx, bia); + + /* work out the 3 partial products */ + if (is_square) + { + p0 = bi_square(ctx, bi_copy(x0)); + p2 = bi_square(ctx, bi_copy(x1)); + p1 = bi_square(ctx, bi_add(ctx, x0, x1)); + } + else /* normal multiply */ + { + bigint *y0, *y1; + y0 = bi_clone(ctx, bib); + y0->size = m; + y1 = bi_clone(ctx, bib); + comp_right_shift(y1, m); + bi_free(ctx, bib); + + p0 = bi_multiply(ctx, bi_copy(x0), bi_copy(y0)); + p2 = bi_multiply(ctx, bi_copy(x1), bi_copy(y1)); + p1 = bi_multiply(ctx, bi_add(ctx, x0, x1), bi_add(ctx, y0, y1)); + } + + p1 = bi_subtract(ctx, + bi_subtract(ctx, p1, bi_copy(p2), NULL), bi_copy(p0), NULL); + + comp_left_shift(p1, m); + comp_left_shift(p2, 2*m); + return bi_add(ctx, p1, bi_add(ctx, p0, p2)); +} +#endif + +/** + * @brief Perform a multiplication operation between two bigints. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @return The result of the multiplication. + */ +bigint * ICACHE_FLASH_ATTR bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib) +{ + check(bia); + check(bib); + +#ifdef CONFIG_BIGINT_KARATSUBA + if (min(bia->size, bib->size) < MUL_KARATSUBA_THRESH) + { + return regular_multiply(ctx, bia, bib, 0, 0); + } + + return karatsuba(ctx, bia, bib, 0); +#else + return regular_multiply(ctx, bia, bib, 0, 0); +#endif +} + +#ifdef CONFIG_BIGINT_SQUARE +/* + * Perform the actual square operion. It takes into account overflow. + */ +static bigint * ICACHE_FLASH_ATTR regular_square(BI_CTX *ctx, bigint *bi) +{ + int t = bi->size; + int i = 0, j; + bigint *biR = alloc(ctx, t*2+1); + comp *w = biR->comps; + comp *x = bi->comps; + long_comp carry; + os_memset(w, 0, biR->size*COMP_BYTE_SIZE); + + do + { + long_comp tmp = w[2*i] + (long_comp)x[i]*x[i]; + w[2*i] = (comp)tmp; + carry = tmp >> COMP_BIT_SIZE; + + for (j = i+1; j < t; j++) + { + uint8_t c = 0; + long_comp xx = (long_comp)x[i]*x[j]; + if ((COMP_MAX-xx) < xx) + c = 1; + + tmp = (xx<<1); + + if ((COMP_MAX-tmp) < w[i+j]) + c = 1; + + tmp += w[i+j]; + + if ((COMP_MAX-tmp) < carry) + c = 1; + + tmp += carry; + w[i+j] = (comp)tmp; + carry = tmp >> COMP_BIT_SIZE; + + if (c) + carry += COMP_RADIX; + } + + tmp = w[i+t] + carry; + w[i+t] = (comp)tmp; + w[i+t+1] = tmp >> COMP_BIT_SIZE; + } while (++i < t); + + bi_free(ctx, bi); + return trim(biR); +} + +/** + * @brief Perform a square operation on a bigint. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @return The result of the multiplication. + */ +bigint * ICACHE_FLASH_ATTR bi_square(BI_CTX *ctx, bigint *bia) +{ + check(bia); + +#ifdef CONFIG_BIGINT_KARATSUBA + if (bia->size < SQU_KARATSUBA_THRESH) + { + return regular_square(ctx, bia); + } + + return karatsuba(ctx, bia, NULL, 1); +#else + return regular_square(ctx, bia); +#endif +} +#endif + +/** + * @brief Compare two bigints. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @return -1 if smaller, 1 if larger and 0 if equal. + */ +int ICACHE_FLASH_ATTR bi_compare(bigint *bia, bigint *bib) +{ + int r, i; + + check(bia); + check(bib); + + if (bia->size > bib->size) + r = 1; + else if (bia->size < bib->size) + r = -1; + else + { + comp *a = bia->comps; + comp *b = bib->comps; + + /* Same number of components. Compare starting from the high end + * and working down. */ + r = 0; + i = bia->size - 1; + + do + { + if (a[i] > b[i]) + { + r = 1; + break; + } + else if (a[i] < b[i]) + { + r = -1; + break; + } + } while (--i >= 0); + } + + return r; +} + +/* + * Allocate and zero more components. Does not consume bi. + */ +static void ICACHE_FLASH_ATTR more_comps(bigint *bi, int n) +{ + if (n > bi->max_comps) + { + bi->max_comps = max(bi->max_comps * 2, n); + bi->comps = (comp*)os_realloc(bi->comps, bi->max_comps * COMP_BYTE_SIZE); + } + + if (n > bi->size) + { + os_memset(&bi->comps[bi->size], 0, (n-bi->size)*COMP_BYTE_SIZE); + } + + bi->size = n; +} + +/* + * Make a new empty bigint. It may just use an old one if one is available. + * Otherwise get one off the heap. + */ +static bigint * ICACHE_FLASH_ATTR alloc(BI_CTX *ctx, int size) +{ + bigint *biR; + + /* Can we recycle an old bigint? */ + if (ctx->free_list != NULL) + { + biR = ctx->free_list; + ctx->free_list = biR->next; + ctx->free_count--; + + if (biR->refs != 0) + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("alloc: refs was not 0\n"); +#endif + return; /* wujg : org ----> abort(); */ + } + + more_comps(biR, size); + } + else + { + /* No free bigints available - create a new one. */ + biR = (bigint *)os_malloc(sizeof(bigint)); + biR->comps = (comp*)os_malloc(size * COMP_BYTE_SIZE); + biR->max_comps = size; /* give some space to spare */ + } + + biR->size = size; + biR->refs = 1; + biR->next = NULL; + ctx->active_count++; + return biR; +} + +/* + * Work out the highest '1' bit in an exponent. Used when doing sliding-window + * exponentiation. + */ +static int ICACHE_FLASH_ATTR find_max_exp_index(bigint *biexp) +{ + int i = COMP_BIT_SIZE-1; + comp shift = COMP_RADIX/2; + comp test = biexp->comps[biexp->size-1]; /* assume no leading zeroes */ + + check(biexp); + + do + { + if (test & shift) + { + return i+(biexp->size-1)*COMP_BIT_SIZE; + } + + shift >>= 1; + } while (i-- != 0); + + return -1; /* error - must have been a leading 0 */ +} + +/* + * Is a particular bit is an exponent 1 or 0? Used when doing sliding-window + * exponentiation. + */ +static int ICACHE_FLASH_ATTR exp_bit_is_one(bigint *biexp, int offset) +{ + comp test = biexp->comps[offset / COMP_BIT_SIZE]; + int num_shifts = offset % COMP_BIT_SIZE; + comp shift = 1; + int i; + + check(biexp); + + for (i = 0; i < num_shifts; i++) + { + shift <<= 1; + } + + return (test & shift) != 0; +} + +#ifdef CONFIG_BIGINT_CHECK_ON +/* + * Perform a sanity check on bi. + */ +static void ICACHE_FLASH_ATTR check(const bigint *bi) +{ + if (bi->refs <= 0) + { + ssl_printf("check: zero or negative refs in bigint\n"); + return; /* wujg : org ----> abort(); */ + } + + if (bi->next != NULL) + { + ssl_printf("check: attempt to use a bigint from " + "the free list\n"); + return; /* wujg : org ----> abort(); */ + } +} +#endif + +/* + * Delete any leading 0's (and allow for 0). + */ +static bigint * ICACHE_FLASH_ATTR trim(bigint *bi) +{ + check(bi); + + while (bi->comps[bi->size-1] == 0 && bi->size > 1) + { + bi->size--; + } + + return bi; +} + +#if defined(CONFIG_BIGINT_MONTGOMERY) +/** + * @brief Perform a single montgomery reduction. + * @param ctx [in] The bigint session context. + * @param bixy [in] A bigint. + * @return The result of the montgomery reduction. + */ +bigint * ICACHE_FLASH_ATTR bi_mont(BI_CTX *ctx, bigint *bixy) +{ + int i = 0, n; + uint8_t mod_offset = ctx->mod_offset; + bigint *bim = ctx->bi_mod[mod_offset]; + comp mod_inv = ctx->N0_dash[mod_offset]; + + check(bixy); + + if (ctx->use_classical) /* just use classical instead */ + { + return bi_mod(ctx, bixy); + } + + n = bim->size; + + do + { + bixy = bi_add(ctx, bixy, comp_left_shift( + bi_int_multiply(ctx, bim, bixy->comps[i]*mod_inv), i)); + } while (++i < n); + + comp_right_shift(bixy, n); + + if (bi_compare(bixy, bim) >= 0) + { + bixy = bi_subtract(ctx, bixy, bim, NULL); + } + + return bixy; +} + +#elif defined(CONFIG_BIGINT_BARRETT) +/* + * Stomp on the most significant components to give the illusion of a "mod base + * radix" operation + */ +static bigint * ICACHE_FLASH_ATTR comp_mod(bigint *bi, int mod) +{ + check(bi); + + if (bi->size > mod) + { + bi->size = mod; + } + + return bi; +} + +/** + * @brief Perform a single Barrett reduction. + * @param ctx [in] The bigint session context. + * @param bi [in] A bigint. + * @return The result of the Barrett reduction. + */ +bigint * ICACHE_FLASH_ATTR bi_barrett(BI_CTX *ctx, bigint *bi) +{ + bigint *q1, *q2, *q3, *r1, *r2, *r; + uint8_t mod_offset = ctx->mod_offset; + bigint *bim = ctx->bi_mod[mod_offset]; + int k = bim->size; + + check(bi); + check(bim); + + /* use Classical method instead - Barrett cannot help here */ + if (bi->size > k*2) + { + return bi_mod(ctx, bi); + } + + q1 = comp_right_shift(bi_clone(ctx, bi), k-1); + + /* do outer partial multiply */ + q2 = regular_multiply(ctx, q1, ctx->bi_mu[mod_offset], 0, k-1); + q3 = comp_right_shift(q2, k+1); + r1 = comp_mod(bi, k+1); + + /* do inner partial multiply */ + r2 = comp_mod(regular_multiply(ctx, q3, bim, k+1, 0), k+1); + r = bi_subtract(ctx, r1, r2, NULL); + + /* if (r >= m) r = r - m; */ + if (bi_compare(r, bim) >= 0) + { + r = bi_subtract(ctx, r, bim, NULL); + } + + return r; +} +#endif /* CONFIG_BIGINT_BARRETT */ + +#ifdef CONFIG_BIGINT_SLIDING_WINDOW +/* + * Work out g1, g3, g5, g7... etc for the sliding-window algorithm + */ +static void ICACHE_FLASH_ATTR precompute_slide_window(BI_CTX *ctx, int window, bigint *g1) +{ + int k = 1, i; + bigint *g2; + + for (i = 0; i < window-1; i++) /* compute 2^(window-1) */ + { + k <<= 1; + } + + ctx->g = (bigint **)os_malloc(k*sizeof(bigint *)); + ctx->g[0] = bi_clone(ctx, g1); + bi_permanent(ctx->g[0]); + g2 = bi_residue(ctx, bi_square(ctx, ctx->g[0])); /* g^2 */ + + for (i = 1; i < k; i++) + { + ctx->g[i] = bi_residue(ctx, bi_multiply(ctx, ctx->g[i-1], bi_copy(g2))); + bi_permanent(ctx->g[i]); + } + + bi_free(ctx, g2); + ctx->window = k; +} +#endif + +/** + * @brief Perform a modular exponentiation. + * + * This function requires bi_set_mod() to have been called previously. This is + * one of the optimisations used for performance. + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint on which to perform the mod power operation. + * @param biexp [in] The bigint exponent. + * @return The result of the mod exponentiation operation + * @see bi_set_mod(). + */ +bigint * ICACHE_FLASH_ATTR bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp) +{ + int i = find_max_exp_index(biexp), j, window_size = 1; + bigint *biR = int_to_bi(ctx, 1); + +#if defined(CONFIG_BIGINT_MONTGOMERY) + uint8_t mod_offset = ctx->mod_offset; + if (!ctx->use_classical) + { + /* preconvert */ + bi = bi_mont(ctx, + bi_multiply(ctx, bi, ctx->bi_RR_mod_m[mod_offset])); /* x' */ + bi_free(ctx, biR); + biR = ctx->bi_R_mod_m[mod_offset]; /* A */ + } +#endif + + check(bi); + check(biexp); + +#ifdef CONFIG_BIGINT_SLIDING_WINDOW + for (j = i; j > 32; j /= 5) /* work out an optimum size */ + window_size++; + + /* work out the slide constants */ + precompute_slide_window(ctx, window_size, bi); +#else /* just one constant */ + ctx->g = (bigint **)os_malloc(sizeof(bigint *)); + ctx->g[0] = bi_clone(ctx, bi); + ctx->window = 1; + bi_permanent(ctx->g[0]); +#endif + + /* if sliding-window is off, then only one bit will be done at a time and + * will reduce to standard left-to-right exponentiation */ + do + { + if (exp_bit_is_one(biexp, i)) + { + int l = i-window_size+1; + int part_exp = 0; + + if (l < 0) /* LSB of exponent will always be 1 */ + l = 0; + else + { + while (exp_bit_is_one(biexp, l) == 0) + l++; /* go back up */ + } + + /* build up the section of the exponent */ + for (j = i; j >= l; j--) + { + biR = bi_residue(ctx, bi_square(ctx, biR)); + if (exp_bit_is_one(biexp, j)) + part_exp++; + + if (j != l) + part_exp <<= 1; + } + + part_exp = (part_exp-1)/2; /* adjust for array */ + biR = bi_residue(ctx, bi_multiply(ctx, biR, ctx->g[part_exp])); + i = l-1; + } + else /* square it */ + { + biR = bi_residue(ctx, bi_square(ctx, biR)); + i--; + } + } while (i >= 0); + + /* cleanup */ + for (i = 0; i < ctx->window; i++) + { + bi_depermanent(ctx->g[i]); + bi_free(ctx, ctx->g[i]); + } + + os_free(ctx->g); + bi_free(ctx, bi); + bi_free(ctx, biexp); +#if defined CONFIG_BIGINT_MONTGOMERY + return ctx->use_classical ? biR : bi_mont(ctx, biR); /* convert back */ +#else /* CONFIG_BIGINT_CLASSICAL or CONFIG_BIGINT_BARRETT */ + return biR; +#endif +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * @brief Perform a modular exponentiation using a temporary modulus. + * + * We need this function to check the signatures of certificates. The modulus + * of this function is temporary as it's just used for authentication. + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint to perform the exp/mod. + * @param bim [in] The temporary modulus. + * @param biexp [in] The bigint exponent. + * @return The result of the mod exponentiation operation + * @see bi_set_mod(). + */ +bigint * ICACHE_FLASH_ATTR bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp) +{ + bigint *biR, *tmp_biR; + + /* Set up a temporary bigint context and transfer what we need between + * them. We need to do this since we want to keep the original modulus + * which is already in this context. This operation is only called when + * doing peer verification, and so is not expensive :-) */ + BI_CTX *tmp_ctx = bi_initialize(); + bi_set_mod(tmp_ctx, bi_clone(tmp_ctx, bim), BIGINT_M_OFFSET); + tmp_biR = bi_mod_power(tmp_ctx, + bi_clone(tmp_ctx, bi), + bi_clone(tmp_ctx, biexp)); + biR = bi_clone(ctx, tmp_biR); + bi_free(tmp_ctx, tmp_biR); + bi_free_mod(tmp_ctx, BIGINT_M_OFFSET); + bi_terminate(tmp_ctx); + + bi_free(ctx, bi); + bi_free(ctx, bim); + bi_free(ctx, biexp); + return biR; +} +#endif + +#ifdef CONFIG_BIGINT_CRT +/** + * @brief Use the Chinese Remainder Theorem to quickly perform RSA decrypts. + * + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint to perform the exp/mod. + * @param dP [in] CRT's dP bigint + * @param dQ [in] CRT's dQ bigint + * @param p [in] CRT's p bigint + * @param q [in] CRT's q bigint + * @param qInv [in] CRT's qInv bigint + * @return The result of the CRT operation + */ +bigint * ICACHE_FLASH_ATTR bi_crt(BI_CTX *ctx, bigint *bi, + bigint *dP, bigint *dQ, + bigint *p, bigint *q, bigint *qInv) +{ + bigint *m1, *m2, *h; + + /* Montgomery has a condition the 0 < x, y < m and these products violate + * that condition. So disable Montgomery when using CRT */ +#if defined(CONFIG_BIGINT_MONTGOMERY) + ctx->use_classical = 1; +#endif + ctx->mod_offset = BIGINT_P_OFFSET; + m1 = bi_mod_power(ctx, bi_copy(bi), dP); + + ctx->mod_offset = BIGINT_Q_OFFSET; + m2 = bi_mod_power(ctx, bi, dQ); + + h = bi_subtract(ctx, bi_add(ctx, m1, p), bi_copy(m2), NULL); + h = bi_multiply(ctx, h, qInv); + ctx->mod_offset = BIGINT_P_OFFSET; + h = bi_residue(ctx, h); +#if defined(CONFIG_BIGINT_MONTGOMERY) + ctx->use_classical = 0; /* reset for any further operation */ +#endif + return bi_add(ctx, m2, bi_multiply(ctx, q, h)); +} +#endif +/** @} */ diff --git a/app/ssl/crypto/ssl_crypto_misc.c b/app/ssl/crypto/ssl_crypto_misc.c new file mode 100644 index 00000000..356179ae --- /dev/null +++ b/app/ssl/crypto/ssl_crypto_misc.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Some misc. routines to help things out + */ + +#include +//#include +//#include +//#include + +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_crypto_misc.h" +#ifdef CONFIG_WIN32_USE_CRYPTO_LIB +#include "wincrypt.h" +#endif + +#ifndef WIN32 +static int rng_fd = -1; +#elif defined(CONFIG_WIN32_USE_CRYPTO_LIB) +static HCRYPTPROV gCryptProv; +#endif + +#if (!defined(CONFIG_USE_DEV_URANDOM) && !defined(CONFIG_WIN32_USE_CRYPTO_LIB)) +/* change to processor registers as appropriate */ +#define ENTROPY_POOL_SIZE 32 +#define ENTROPY_COUNTER1 ((((uint64_t)tv.tv_sec)<<32) | tv.tv_usec) +#define ENTROPY_COUNTER2 rand() +static uint8_t entropy_pool[ENTROPY_POOL_SIZE]; +#endif + +const char * const unsupported_str = "Error: Feature not supported\n"; + +#ifndef CONFIG_SSL_SKELETON_MODE +/** + * Retrieve a file and put it into memory + * @return The size of the file, or -1 on failure. + */ +int get_file(const char *filename, uint8_t **buf) +{ +#if 0 + int total_bytes = 0; + int bytes_read = 0; + int filesize; + FILE *stream = fopen(filename, "rb"); + + if (stream == NULL) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("file '%s' does not exist\n", filename); //TTY_FLUSH(); +#endif + return -1; + } + + /* Win CE doesn't support stat() */ + fseek(stream, 0, SEEK_END); + filesize = ftell(stream); + *buf = (uint8_t *)os_malloc(filesize); + fseek(stream, 0, SEEK_SET); + + do + { + bytes_read = fread(*buf+total_bytes, 1, filesize-total_bytes, stream); + total_bytes += bytes_read; + } while (total_bytes < filesize && bytes_read > 0); + + fclose(stream); + return filesize; +#endif + + return 0; +} +#endif + +/** + * Initialise the Random Number Generator engine. + * - On Win32 use the platform SDK's crypto engine. + * - On Linux use /dev/urandom + * - If none of these work then use a custom RNG. + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR RNG_initialize() +{ +#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM) + rng_fd = ax_open("/dev/urandom", O_RDONLY); +#elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB) + if (!CryptAcquireContext(&gCryptProv, + NULL, NULL, PROV_RSA_FULL, 0)) + { + if (GetLastError() == NTE_BAD_KEYSET && + !CryptAcquireContext(&gCryptProv, + NULL, + NULL, + PROV_RSA_FULL, + CRYPT_NEWKEYSET)) + { + printf("CryptoLib: %x\n", unsupported_str, GetLastError()); + exit(1); + } + } +#else + /* start of with a stack to copy across */ + int i; + os_memcpy(entropy_pool, &i, ENTROPY_POOL_SIZE); + srand((unsigned int)&i); +#endif +} + +/** + * If no /dev/urandom, then initialise the RNG with something interesting. + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR RNG_custom_init(const uint8_t *seed_buf, int size) +{ +#if defined(WIN32) || defined(CONFIG_WIN32_USE_CRYPTO_LIB) + int i; + + for (i = 0; i < ENTROPY_POOL_SIZE && i < size; i++) + entropy_pool[i] ^= seed_buf[i]; +#endif +} + +/** + * Terminate the RNG engine. + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR RNG_terminate(void) +{ +#ifndef WIN32 +// close(rng_fd); +#elif defined(CONFIG_WIN32_USE_CRYPTO_LIB) + CryptReleaseContext(gCryptProv, 0); +#endif +} + +/** + * Set a series of bytes with a random number. Individual bytes can be 0 + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR get_random(int num_rand_bytes, uint8_t *rand_data) +{ +#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM) + /* use the Linux default */ + read(rng_fd, rand_data, num_rand_bytes); /* read from /dev/urandom */ +#elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB) + /* use Microsoft Crypto Libraries */ + CryptGenRandom(gCryptProv, num_rand_bytes, rand_data); +#else /* nothing else to use, so use a custom RNG */ + /* The method we use when we've got nothing better. Use RC4, time + and a couple of random seeds to generate a random sequence */ + RC4_CTX rng_ctx; + struct timeval tv; + MD5_CTX rng_digest_ctx; + uint8_t digest[MD5_SIZE]; + uint64_t *ep; + int i; + + /* A proper implementation would use counters etc for entropy */ +// gettimeofday(&tv, NULL); + ep = (uint64_t *)entropy_pool; + ep[0] ^= ENTROPY_COUNTER1; + ep[1] ^= ENTROPY_COUNTER2; + + /* use a digested version of the entropy pool as a key */ + MD5_Init(&rng_digest_ctx); + MD5_Update(&rng_digest_ctx, entropy_pool, ENTROPY_POOL_SIZE); + MD5_Final(digest, &rng_digest_ctx); + + /* come up with the random sequence */ + RC4_setup(&rng_ctx, digest, MD5_SIZE); /* use as a key */ + os_memcpy(rand_data, entropy_pool, num_rand_bytes < ENTROPY_POOL_SIZE ? + num_rand_bytes : ENTROPY_POOL_SIZE); + RC4_crypt(&rng_ctx, rand_data, rand_data, num_rand_bytes); + + /* move things along */ + for (i = ENTROPY_POOL_SIZE-1; i >= MD5_SIZE ; i--) + entropy_pool[i] = entropy_pool[i-MD5_SIZE]; + + /* insert the digest at the start of the entropy pool */ + os_memcpy(entropy_pool, digest, MD5_SIZE); +#endif +} + +/** + * Set a series of bytes with a random number. Individual bytes are not zero. + */ +void ICACHE_FLASH_ATTR get_random_NZ(int num_rand_bytes, uint8_t *rand_data) +{ + int i; + get_random(num_rand_bytes, rand_data); + + for (i = 0; i < num_rand_bytes; i++) + { + while (rand_data[i] == 0) /* can't be 0 */ + rand_data[i] = (uint8_t)(rand()); + } +} + +/** + * Some useful diagnostic routines + */ +#if defined(CONFIG_SSL_FULL_MODE) || defined(CONFIG_DEBUG) +int hex_finish; +int hex_index; + +static void ICACHE_FLASH_ATTR print_hex_init(int finish) +{ + hex_finish = finish; + hex_index = 0; +} + +static void ICACHE_FLASH_ATTR print_hex(uint8_t hex) +{ + static int column; + + if (hex_index == 0) + { + column = 0; + } + + ssl_printf("%02x ", hex); + if (++column == 8) + { + ssl_printf(": "); + } + else if (column >= 16) + { + ssl_printf("\n"); + column = 0; + } + + if (++hex_index >= hex_finish && column > 0) + { + ssl_printf("\n"); + } +} + +/** + * Spit out a blob of data for diagnostics. The data is is a nice column format + * for easy reading. + * + * @param format [in] The string (with possible embedded format characters) + * @param size [in] The number of numbers to print + * @param data [in] The start of data to use + * @param ... [in] Any additional arguments + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR print_blob(const char *format, + const uint8_t *data, int size, ...) +{ +// int i; +// char tmp[80]; +// va_list(ap); + +// va_start(ap, size); +// sprintf(tmp, "%s\n", format); +// vprintf(tmp, ap); +// print_hex_init(size); +// for (i = 0; i < size; i++) +// { +// print_hex(data[i]); +// } + +// va_end(ap); +// TTY_FLUSH(); +} +#elif defined(WIN32) +/* VC6.0 doesn't handle variadic macros */ +EXP_FUNC void STDCALL print_blob(const char *format, const unsigned char *data, + int size, ...) {} +#endif + +#if defined(CONFIG_SSL_HAS_PEM) || defined(CONFIG_HTTP_HAS_AUTHORIZATION) +/* base64 to binary lookup table */ +static const uint8_t map[128] = +{ + 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, 254, 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 +}; + +EXP_FUNC int STDCALL ICACHE_FLASH_ATTR base64_decode(const char *in, int len, + uint8_t *out, int *outlen) +{ + int g, t, x, y, z; + uint8_t c; + int ret = -1; + + 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) + goto error; + } + else if (g != 3) /* only allow = at end */ + goto error; + + 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) + goto error; + } + + if (y != 0) + goto error; + + *outlen = z; + ret = 0; + +error: +#ifdef CONFIG_SSL_FULL_MODE + if (ret < 0) + ssl_printf("Error: Invalid base64\n"); //TTY_FLUSH(); +#endif + //TTY_FLUSH(); + return ret; + +} +#endif + diff --git a/app/ssl/crypto/ssl_hmac.c b/app/ssl/crypto/ssl_hmac.c new file mode 100644 index 00000000..caff88a3 --- /dev/null +++ b/app/ssl/crypto/ssl_hmac.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * HMAC implementation - This code was originally taken from RFC2104 + * See http://www.ietf.org/rfc/rfc2104.txt and + * http://www.faqs.org/rfcs/rfc2202.html + */ + +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_crypto.h" + +/** + * Perform HMAC-MD5 + * NOTE: does not handle keys larger than the block size. + */ +void ICACHE_FLASH_ATTR ssl_hmac_md5(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest) +{ + MD5_CTX context; + uint8_t k_ipad[64]; + uint8_t k_opad[64]; + int i; + + os_memset(k_ipad, 0, sizeof k_ipad); + os_memset(k_opad, 0, sizeof k_opad); + os_memcpy(k_ipad, key, key_len); + os_memcpy(k_opad, key, key_len); + + for (i = 0; i < 64; i++) + { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + MD5_Init(&context); + MD5_Update(&context, k_ipad, 64); + MD5_Update(&context, msg, length); + MD5_Final(digest, &context); + MD5_Init(&context); + MD5_Update(&context, k_opad, 64); + MD5_Update(&context, digest, MD5_SIZE); + MD5_Final(digest, &context); +} + +/** + * Perform HMAC-SHA1 + * NOTE: does not handle keys larger than the block size. + */ +void ICACHE_FLASH_ATTR ssl_hmac_sha1(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest) +{ + SHA1_CTX context; + uint8_t k_ipad[64]; + uint8_t k_opad[64]; + int i; + + os_memset(k_ipad, 0, sizeof k_ipad); + os_memset(k_opad, 0, sizeof k_opad); + os_memcpy(k_ipad, key, key_len); + os_memcpy(k_opad, key, key_len); + + for (i = 0; i < 64; i++) + { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + SHA1_Init(&context); + SHA1_Update(&context, k_ipad, 64); + SHA1_Update(&context, msg, length); + SHA1_Final(digest, &context); + SHA1_Init(&context); + SHA1_Update(&context, k_opad, 64); + SHA1_Update(&context, digest, SHA1_SIZE); + SHA1_Final(digest, &context); +} diff --git a/app/ssl/crypto/ssl_md2.c b/app/ssl/crypto/ssl_md2.c new file mode 100644 index 00000000..d142230d --- /dev/null +++ b/app/ssl/crypto/ssl_md2.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * RFC 1115/1319 compliant MD2 implementation + * The MD2 algorithm was designed by Ron Rivest in 1989. + * + * http://www.ietf.org/rfc/rfc1115.txt + * http://www.ietf.org/rfc/rfc1319.txt + */ + +//#include +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_crypto.h" +//#include "os.h" +#include "lwip/mem.h" +/** + * This code is only here to enable the verification of Verisign root + * certificates. So only enable it for verification mode. + */ +#ifdef CONFIG_SSL_CERT_VERIFICATION + +static const uint8_t PI_SUBST[256] = +{ + 0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36, + 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13, 0x62, 0xA7, 0x05, 0xF3, + 0xC0, 0xC7, 0x73, 0x8C, 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, + 0x82, 0xCA, 0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16, + 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12, 0xBE, 0x4E, + 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, 0xA0, 0xFB, 0xF5, 0x8E, + 0xBB, 0x2F, 0xEE, 0x7A, 0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, + 0x07, 0x3F, 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21, + 0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, 0x35, 0x3E, + 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03, 0xFF, 0x19, 0x30, 0xB3, + 0x48, 0xA5, 0xB5, 0xD1, 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, + 0xAA, 0xC6, 0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6, + 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1, 0x45, 0x9D, + 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5B, 0xCF, 0x65, + 0xE6, 0x2D, 0xA8, 0x02, 0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, + 0xB9, 0xF6, 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F, + 0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, 0xC3, 0x5C, + 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26, 0x2C, 0x53, 0x0D, 0x6E, + 0x85, 0x28, 0x84, 0x09, 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, + 0x4D, 0x52, 0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA, + 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A, 0x78, 0x88, + 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, 0xE9, 0xCB, 0xD5, 0xFE, + 0x3B, 0x00, 0x1D, 0x39, 0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, + 0xD0, 0xE4, 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A, + 0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, 0xDB, 0x99, + 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14 +}; + +/* + * MD2 context setup + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR MD2_Init(MD2_CTX *ctx) +{ + os_memset(ctx, 0, sizeof *ctx); +} + +static void ICACHE_FLASH_ATTR md2_process(MD2_CTX *ctx) +{ + int i, j; + uint8_t t = 0; + + for (i = 0; i < 16; i++) + { + ctx->state[i + 16] = ctx->buffer[i]; + ctx->state[i + 32] = ctx->buffer[i] ^ ctx->state[i]; + } + + for (i = 0; i < 18; i++) + { + for (j = 0; j < 48; j++) + t = (ctx->state[j] ^= PI_SUBST[t]); + + t = (t + i) & 0xFF; + } + + t = ctx->cksum[15]; + + for (i = 0; i < 16; i++) + t = (ctx->cksum[i] ^= PI_SUBST[ctx->buffer[i] ^ t]); +} + +/* + * MD2 process buffer + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR MD2_Update(MD2_CTX *ctx, const uint8_t *input, int ilen) +{ + int fill; + + while (ilen > 0) + { + if (ctx->left + ilen > 16) + fill = 16 - ctx->left; + else + fill = ilen; + + os_memcpy(ctx->buffer + ctx->left, input, fill); + + ctx->left += fill; + input += fill; + ilen -= fill; + + if (ctx->left == 16) + { + ctx->left = 0; + md2_process(ctx); + } + } +} + +/* + * MD2 final digest + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR MD2_Final(uint8_t *output, MD2_CTX *ctx) +{ + int i; + uint8_t x; + + x = (uint8_t)(16 - ctx->left); + + for (i = ctx->left; i < 16; i++) + ctx->buffer[i] = x; + + md2_process(ctx); + + os_memcpy(ctx->buffer, ctx->cksum, 16); + md2_process(ctx); + + os_memcpy(output, ctx->state, 16); +} + +#endif diff --git a/app/ssl/crypto/ssl_md5.c b/app/ssl/crypto/ssl_md5.c new file mode 100644 index 00000000..3167e719 --- /dev/null +++ b/app/ssl/crypto/ssl_md5.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * This file implements the MD5 algorithm as defined in RFC1321 + */ + +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_crypto.h" +//#include "os.h" +#include "lwip/mem.h" +/* Constants for MD5Transform routine. + */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +/* ----- static functions ----- */ +static void MD5Transform(uint32_t state[4], const uint8_t block[64]); +static void Encode(uint8_t *output, uint32_t *input, uint32_t len); +static void Decode(uint32_t *output, const uint8_t *input, uint32_t len); + +static const uint8_t PADDING[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + Rotation is separate from addition to prevent recomputation. */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/** + * MD5 initialization - begins an MD5 operation, writing a new ctx. + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR MD5_Init(MD5_CTX *ctx) +{ + ctx->count[0] = ctx->count[1] = 0; + + /* Load magic initialization constants. + */ + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +/** + * Accepts an array of octets as the next portion of the message. + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR MD5_Update(MD5_CTX *ctx, const uint8_t * msg, int len) +{ + uint32_t x; + int i, partLen; + + /* Compute number of bytes mod 64 */ + x = (uint32_t)((ctx->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((ctx->count[0] += ((uint32_t)len << 3)) < ((uint32_t)len << 3)) + ctx->count[1]++; + ctx->count[1] += ((uint32_t)len >> 29); + + partLen = 64 - x; + + /* Transform as many times as possible. */ + if (len >= partLen) + { + os_memcpy(&ctx->buffer[x], msg, partLen); + MD5Transform(ctx->state, ctx->buffer); + + for (i = partLen; i + 63 < len; i += 64) + MD5Transform(ctx->state, &msg[i]); + + x = 0; + } + else + i = 0; + + /* Buffer remaining input */ + os_memcpy(&ctx->buffer[x], &msg[i], len-i); +} + +/** + * Return the 128-bit message digest into the user's array + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR MD5_Final(uint8_t *digest, MD5_CTX *ctx) +{ + uint8_t bits[8]; + uint32_t x, padLen; + + /* Save number of bits */ + Encode(bits, ctx->count, 8); + + /* Pad out to 56 mod 64. + */ + x = (uint32_t)((ctx->count[0] >> 3) & 0x3f); + padLen = (x < 56) ? (56 - x) : (120 - x); + MD5_Update(ctx, PADDING, padLen); + + /* Append length (before padding) */ + MD5_Update(ctx, bits, 8); + + /* Store state in digest */ + Encode(digest, ctx->state, MD5_SIZE); +} + +/** + * MD5 basic transformation. Transforms state based on block. + */ +static void ICACHE_FLASH_ATTR MD5Transform(uint32_t state[4], const uint8_t block[64]) +{ + uint32_t a = state[0], b = state[1], c = state[2], + d = state[3], x[MD5_SIZE]; + + Decode(x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +/** + * Encodes input (uint32_t) into output (uint8_t). Assumes len is + * a multiple of 4. + */ +static void ICACHE_FLASH_ATTR Encode(uint8_t *output, uint32_t *input, uint32_t len) +{ + uint32_t i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + output[j] = (uint8_t)(input[i] & 0xff); + output[j+1] = (uint8_t)((input[i] >> 8) & 0xff); + output[j+2] = (uint8_t)((input[i] >> 16) & 0xff); + output[j+3] = (uint8_t)((input[i] >> 24) & 0xff); + } +} + +/** + * Decodes input (uint8_t) into output (uint32_t). Assumes len is + * a multiple of 4. + */ +static void ICACHE_FLASH_ATTR Decode(uint32_t *output, const uint8_t *input, uint32_t len) +{ + uint32_t i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) | + (((uint32_t)input[j+2]) << 16) | (((uint32_t)input[j+3]) << 24); +} diff --git a/app/ssl/crypto/ssl_rc4.c b/app/ssl/crypto/ssl_rc4.c new file mode 100644 index 00000000..eeba43af --- /dev/null +++ b/app/ssl/crypto/ssl_rc4.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * An implementation of the RC4/ARC4 algorithm. + * Originally written by Christophe Devine. + */ + +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_crypto.h" + +/** + * Get ready for an encrypt/decrypt operation + */ +void ICACHE_FLASH_ATTR RC4_setup(RC4_CTX *ctx, const uint8_t *key, int length) +{ + int i, j = 0, k = 0, a; + uint8_t *m; + + ctx->x = 0; + ctx->y = 0; + m = ctx->m; + + for (i = 0; i < 256; i++) + m[i] = i; + + for (i = 0; i < 256; i++) + { + a = m[i]; + j = (uint8_t)(j + a + key[k]); + m[i] = m[j]; + m[j] = a; + + if (++k >= length) + k = 0; + } +} + +/** + * Perform the encrypt/decrypt operation (can use it for either since + * this is a stream cipher). + * NOTE: *msg and *out must be the same pointer (performance tweak) + */ +void ICACHE_FLASH_ATTR RC4_crypt(RC4_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ + int i; + uint8_t *m, x, y, a, b; + + x = ctx->x; + y = ctx->y; + m = ctx->m; + + for (i = 0; i < length; i++) + { + a = m[++x]; + y += a; + m[x] = b = m[y]; + m[y] = a; + out[i] ^= m[(uint8_t)(a + b)]; + } + + ctx->x = x; + ctx->y = y; +} diff --git a/app/ssl/crypto/ssl_rsa.c b/app/ssl/crypto/ssl_rsa.c new file mode 100644 index 00000000..f9a2a848 --- /dev/null +++ b/app/ssl/crypto/ssl_rsa.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Implements the RSA public encryption algorithm. Uses the bigint library to + * perform its calculations. + */ + +//#include +//#include +//#include +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_crypto.h" +//#include "os.h" +#include "lwip/mem.h" +void ICACHE_FLASH_ATTR RSA_priv_key_new(RSA_CTX **ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len, + const uint8_t *priv_exp, int priv_len +#if CONFIG_BIGINT_CRT + , const uint8_t *p, int p_len, + const uint8_t *q, int q_len, + const uint8_t *dP, int dP_len, + const uint8_t *dQ, int dQ_len, + const uint8_t *qInv, int qInv_len +#endif + ) +{ + RSA_CTX *rsa_ctx; + BI_CTX *bi_ctx; + RSA_pub_key_new(ctx, modulus, mod_len, pub_exp, pub_len); + rsa_ctx = *ctx; + bi_ctx = rsa_ctx->bi_ctx; + rsa_ctx->d = bi_import(bi_ctx, priv_exp, priv_len); + bi_permanent(rsa_ctx->d); + +#ifdef CONFIG_BIGINT_CRT + rsa_ctx->p = bi_import(bi_ctx, p, p_len); + rsa_ctx->q = bi_import(bi_ctx, q, q_len); + rsa_ctx->dP = bi_import(bi_ctx, dP, dP_len); + rsa_ctx->dQ = bi_import(bi_ctx, dQ, dQ_len); + rsa_ctx->qInv = bi_import(bi_ctx, qInv, qInv_len); + bi_permanent(rsa_ctx->dP); + bi_permanent(rsa_ctx->dQ); + bi_permanent(rsa_ctx->qInv); + bi_set_mod(bi_ctx, rsa_ctx->p, BIGINT_P_OFFSET); + bi_set_mod(bi_ctx, rsa_ctx->q, BIGINT_Q_OFFSET); +#endif +} + +void ICACHE_FLASH_ATTR RSA_pub_key_new(RSA_CTX **ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len) +{ + RSA_CTX *rsa_ctx; + BI_CTX *bi_ctx; + + if (*ctx) /* if we load multiple certs, dump the old one */ + RSA_free(*ctx); + + bi_ctx = bi_initialize(); + *ctx = (RSA_CTX *)os_zalloc(sizeof(RSA_CTX)); + rsa_ctx = *ctx; + rsa_ctx->bi_ctx = bi_ctx; + rsa_ctx->num_octets = mod_len; + rsa_ctx->m = bi_import(bi_ctx, modulus, mod_len); + bi_set_mod(bi_ctx, rsa_ctx->m, BIGINT_M_OFFSET); + rsa_ctx->e = bi_import(bi_ctx, pub_exp, pub_len); + bi_permanent(rsa_ctx->e); +} + +/** + * Free up any RSA context resources. + */ +void ICACHE_FLASH_ATTR RSA_free(RSA_CTX *rsa_ctx) +{ + BI_CTX *bi_ctx; + if (rsa_ctx == NULL) /* deal with ptrs that are null */ + return; + + bi_ctx = rsa_ctx->bi_ctx; + + bi_depermanent(rsa_ctx->e); + bi_free(bi_ctx, rsa_ctx->e); + bi_free_mod(rsa_ctx->bi_ctx, BIGINT_M_OFFSET); + + if (rsa_ctx->d) + { + bi_depermanent(rsa_ctx->d); + bi_free(bi_ctx, rsa_ctx->d); +#ifdef CONFIG_BIGINT_CRT + bi_depermanent(rsa_ctx->dP); + bi_depermanent(rsa_ctx->dQ); + bi_depermanent(rsa_ctx->qInv); + bi_free(bi_ctx, rsa_ctx->dP); + bi_free(bi_ctx, rsa_ctx->dQ); + bi_free(bi_ctx, rsa_ctx->qInv); + bi_free_mod(rsa_ctx->bi_ctx, BIGINT_P_OFFSET); + bi_free_mod(rsa_ctx->bi_ctx, BIGINT_Q_OFFSET); +#endif + } + + bi_terminate(bi_ctx); + os_free(rsa_ctx); +} + +/** + * @brief Use PKCS1.5 for decryption/verification. + * @param ctx [in] The context + * @param in_data [in] The data to encrypt (must be < modulus size-11) + * @param out_data [out] The encrypted data. + * @param is_decryption [in] Decryption or verify operation. + * @return The number of bytes that were originally encrypted. -1 on error. + * @see http://www.rsasecurity.com/rsalabs/node.asp?id=2125 + */ +int ICACHE_FLASH_ATTR RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, + uint8_t *out_data, int is_decryption) +{ + const int byte_size = ctx->num_octets; + int i, size; + bigint *decrypted_bi, *dat_bi; + uint8_t *block = (uint8_t *)os_malloc(byte_size); + + os_memset(out_data, 0, byte_size); /* initialise */ + + /* decrypt */ + dat_bi = bi_import(ctx->bi_ctx, in_data, byte_size); +#ifdef CONFIG_SSL_CERT_VERIFICATION + decrypted_bi = is_decryption ? /* decrypt or verify? */ + RSA_private(ctx, dat_bi) : RSA_public(ctx, dat_bi); +#else /* always a decryption */ + decrypted_bi = RSA_private(ctx, dat_bi); +#endif + + /* convert to a normal block */ + bi_export(ctx->bi_ctx, decrypted_bi, block, byte_size); + + i = 10; /* start at the first possible non-padded byte */ + +#ifdef CONFIG_SSL_CERT_VERIFICATION + if (is_decryption == 0) /* PKCS1.5 signing pads with "0xff"s */ + { + while (block[i++] == 0xff && i < byte_size); + + if (block[i-2] != 0xff) + i = byte_size; /*ensure size is 0 */ + } + else /* PKCS1.5 encryption padding is random */ +#endif + { + while (block[i++] && i < byte_size); + } + size = byte_size - i; + + /* get only the bit we want */ + if (size > 0) + os_memcpy(out_data, &block[i], size); + + os_free(block); + return size ? size : -1; +} + +/** + * Performs m = c^d mod n + */ +bigint *ICACHE_FLASH_ATTR RSA_private(const RSA_CTX *c, bigint *bi_msg) +{ +#ifdef CONFIG_BIGINT_CRT + return bi_crt(c->bi_ctx, bi_msg, c->dP, c->dQ, c->p, c->q, c->qInv); +#else + BI_CTX *ctx = c->bi_ctx; + ctx->mod_offset = BIGINT_M_OFFSET; + return bi_mod_power(ctx, bi_msg, c->d); +#endif +} + +#ifdef CONFIG_SSL_FULL_MODE +/** + * Used for diagnostics. + */ +void ICACHE_FLASH_ATTR RSA_print(const RSA_CTX *rsa_ctx) +{ + if (rsa_ctx == NULL) + return; + + ssl_printf("----------------- RSA DEBUG ----------------\n"); + ssl_printf("Size:\t%d\n", rsa_ctx->num_octets); + bi_print("Modulus", rsa_ctx->m); + bi_print("Public Key", rsa_ctx->e); + bi_print("Private Key", rsa_ctx->d); +} +#endif + +#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT) +/** + * Performs c = m^e mod n + */ +bigint *ICACHE_FLASH_ATTR RSA_public(const RSA_CTX * c, bigint *bi_msg) +{ + c->bi_ctx->mod_offset = BIGINT_M_OFFSET; + return bi_mod_power(c->bi_ctx, bi_msg, c->e); +} + +/** + * Use PKCS1.5 for encryption/signing. + * see http://www.rsasecurity.com/rsalabs/node.asp?id=2125 + */ +int ICACHE_FLASH_ATTR RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, + uint8_t *out_data, int is_signing) +{ + int byte_size = ctx->num_octets; + int num_pads_needed = byte_size-in_len-3; + bigint *dat_bi, *encrypt_bi; + + /* note: in_len+11 must be > byte_size */ + out_data[0] = 0; /* ensure encryption block is < modulus */ + + if (is_signing) + { + out_data[1] = 1; /* PKCS1.5 signing pads with "0xff"'s */ + os_memset(&out_data[2], 0xff, num_pads_needed); + } + else /* randomize the encryption padding with non-zero bytes */ + { + out_data[1] = 2; + get_random_NZ(num_pads_needed, &out_data[2]); + } + + out_data[2+num_pads_needed] = 0; + os_memcpy(&out_data[3+num_pads_needed], in_data, in_len); + + /* now encrypt it */ + dat_bi = bi_import(ctx->bi_ctx, out_data, byte_size); + encrypt_bi = is_signing ? RSA_private(ctx, dat_bi) : + RSA_public(ctx, dat_bi); + bi_export(ctx->bi_ctx, encrypt_bi, out_data, byte_size); + + /* save a few bytes of memory */ + bi_clear_cache(ctx->bi_ctx); + return byte_size; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ diff --git a/app/ssl/crypto/ssl_sha1.c b/app/ssl/crypto/ssl_sha1.c new file mode 100644 index 00000000..6c3de9c1 --- /dev/null +++ b/app/ssl/crypto/ssl_sha1.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * SHA1 implementation - as defined in FIPS PUB 180-1 published April 17, 1995. + * This code was originally taken from RFC3174 + */ + +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_crypto.h" +//#include "os.h" +#include "lwip/mem.h" +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* ----- static functions ----- */ +static void SHA1PadMessage(SHA1_CTX *ctx); +static void SHA1ProcessMessageBlock(SHA1_CTX *ctx); + +/** + * Initialize the SHA1 context + */ +void ICACHE_FLASH_ATTR SHA1_Init(SHA1_CTX *ctx) +{ + ctx->Length_Low = 0; + ctx->Length_High = 0; + ctx->Message_Block_Index = 0; + ctx->Intermediate_Hash[0] = 0x67452301; + ctx->Intermediate_Hash[1] = 0xEFCDAB89; + ctx->Intermediate_Hash[2] = 0x98BADCFE; + ctx->Intermediate_Hash[3] = 0x10325476; + ctx->Intermediate_Hash[4] = 0xC3D2E1F0; +} + +/** + * Accepts an array of octets as the next portion of the message. + */ +void ICACHE_FLASH_ATTR SHA1_Update(SHA1_CTX *ctx, const uint8_t *msg, int len) +{ + while (len--) + { + ctx->Message_Block[ctx->Message_Block_Index++] = (*msg & 0xFF); + ctx->Length_Low += 8; + + if (ctx->Length_Low == 0) + ctx->Length_High++; + + if (ctx->Message_Block_Index == 64) + SHA1ProcessMessageBlock(ctx); + + msg++; + } +} + +/** + * Return the 160-bit message digest into the user's array + */ +void ICACHE_FLASH_ATTR SHA1_Final(uint8_t *digest, SHA1_CTX *ctx) +{ + int i; + + SHA1PadMessage(ctx); + os_memset(ctx->Message_Block, 0, 64); + ctx->Length_Low = 0; /* and clear length */ + ctx->Length_High = 0; + + for (i = 0; i < SHA1_SIZE; i++) + { + digest[i] = ctx->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ); + } +} + +/** + * Process the next 512 bits of the message stored in the array. + */ +static void ICACHE_FLASH_ATTR SHA1ProcessMessageBlock(SHA1_CTX *ctx) +{ + const uint32_t K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = 0; t < 16; t++) + { + W[t] = ctx->Message_Block[t * 4] << 24; + W[t] |= ctx->Message_Block[t * 4 + 1] << 16; + W[t] |= ctx->Message_Block[t * 4 + 2] << 8; + W[t] |= ctx->Message_Block[t * 4 + 3]; + } + + for (t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = ctx->Intermediate_Hash[0]; + B = ctx->Intermediate_Hash[1]; + C = ctx->Intermediate_Hash[2]; + D = ctx->Intermediate_Hash[3]; + E = ctx->Intermediate_Hash[4]; + + for (t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + + B = A; + A = temp; + } + + for (t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + ctx->Intermediate_Hash[0] += A; + ctx->Intermediate_Hash[1] += B; + ctx->Intermediate_Hash[2] += C; + ctx->Intermediate_Hash[3] += D; + ctx->Intermediate_Hash[4] += E; + ctx->Message_Block_Index = 0; +} + +/* + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * @param ctx [in, out] The SHA1 context + */ +static void ICACHE_FLASH_ATTR SHA1PadMessage(SHA1_CTX *ctx) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (ctx->Message_Block_Index > 55) + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0x80; + while(ctx->Message_Block_Index < 64) + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(ctx); + + while (ctx->Message_Block_Index < 56) + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0; + } + } + else + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0x80; + while(ctx->Message_Block_Index < 56) + { + + ctx->Message_Block[ctx->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + ctx->Message_Block[56] = ctx->Length_High >> 24; + ctx->Message_Block[57] = ctx->Length_High >> 16; + ctx->Message_Block[58] = ctx->Length_High >> 8; + ctx->Message_Block[59] = ctx->Length_High; + ctx->Message_Block[60] = ctx->Length_Low >> 24; + ctx->Message_Block[61] = ctx->Length_Low >> 16; + ctx->Message_Block[62] = ctx->Length_Low >> 8; + ctx->Message_Block[63] = ctx->Length_Low; + SHA1ProcessMessageBlock(ctx); +} diff --git a/app/ssl/ssl/Makefile b/app/ssl/ssl/Makefile new file mode 100644 index 00000000..6433ee25 --- /dev/null +++ b/app/ssl/ssl/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = libsslssl.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/ssl/ssl/ssl_asn1.c b/app/ssl/ssl/ssl_asn1.c new file mode 100644 index 00000000..784432d2 --- /dev/null +++ b/app/ssl/ssl/ssl_asn1.c @@ -0,0 +1,591 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Some primitive asn methods for extraction ASN.1 data. + */ + +//#include +//#include +//#include +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_crypto.h" +#include "ssl/ssl_crypto_misc.h" +//#include "os.h" +#include "lwip/mem.h" +struct tm +{ + int tm_sec; /* Seconds. [0-60] (1 leap second) */ + int tm_min; /* Minutes. [0-59] */ + int tm_hour; /* Hours. [0-23] */ + int tm_mday; /* Day. [1-31] */ + int tm_mon; /* Month. [0-11] */ + int tm_year; /* Year - 1900. */ + int tm_wday; /* Day of week. [0-6] */ + int tm_yday; /* Days in year.[0-365] */ + int tm_isdst; /* DST. [-1/0/1]*/ + +#ifdef __USE_BSD + long int tm_gmtoff; /* Seconds east of UTC. */ + __const char *tm_zone; /* Timezone abbreviation. */ +#else + long int __tm_gmtoff; /* Seconds east of UTC. */ + __const char *__tm_zone; /* Timezone abbreviation. */ +#endif +}; + + +#define SIG_OID_PREFIX_SIZE 8 +#define SIG_IIS6_OID_SIZE 5 +#define SIG_SUBJECT_ALT_NAME_SIZE 3 + +/* Must be an RSA algorithm with either SHA1 or MD5 for verifying to work */ +static const uint8_t sig_oid_prefix[SIG_OID_PREFIX_SIZE] = +{ + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01 +}; + +static const uint8_t sig_sha1WithRSAEncrypt[SIG_IIS6_OID_SIZE] = +{ + 0x2b, 0x0e, 0x03, 0x02, 0x1d +}; + +static const uint8_t sig_subject_alt_name[SIG_SUBJECT_ALT_NAME_SIZE] = +{ + 0x55, 0x1d, 0x11 +}; + +/* CN, O, OU */ +static const uint8_t g_dn_types[] = { 3, 10, 11 }; + +int ICACHE_FLASH_ATTR get_asn1_length(const uint8_t *buf, int *offset) +{ + int len, i; + + if (!(buf[*offset] & 0x80)) /* short form */ + { + len = buf[(*offset)++]; + } + else /* long form */ + { + int length_bytes = buf[(*offset)++]&0x7f; + len = 0; + for (i = 0; i < length_bytes; i++) + { + len <<= 8; + len += buf[(*offset)++]; + } + } + + return len; +} + +/** + * Skip the ASN1.1 object type and its length. Get ready to read the object's + * data. + */ +int ICACHE_FLASH_ATTR asn1_next_obj(const uint8_t *buf, int *offset, int obj_type) +{ + if (buf[*offset] != obj_type) + return X509_NOT_OK; + (*offset)++; + return get_asn1_length(buf, offset); +} + +/** + * Skip over an ASN.1 object type completely. Get ready to read the next + * object. + */ +int ICACHE_FLASH_ATTR asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type) +{ + int len; + + if (buf[*offset] != obj_type) + return X509_NOT_OK; + (*offset)++; + len = get_asn1_length(buf, offset); + *offset += len; + return 0; +} + +/** + * Read an integer value for ASN.1 data + * Note: This function allocates memory which must be freed by the user. + */ +int ICACHE_FLASH_ATTR asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object) +{ + int len; + + if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0) + goto end_int_array; + + if (len > 1 && buf[*offset] == 0x00) /* ignore the negative byte */ + { + len--; + (*offset)++; + } + + *object = (uint8_t *)os_malloc(len); + os_memcpy(*object, &buf[*offset], len); + *offset += len; + +end_int_array: + return len; +} + +/** + * Get all the RSA private key specifics from an ASN.1 encoded file + */ +int ICACHE_FLASH_ATTR asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx) +{ + int offset = 7; + uint8_t *modulus = NULL, *priv_exp = NULL, *pub_exp = NULL; + int mod_len, priv_len, pub_len; +#ifdef CONFIG_BIGINT_CRT + uint8_t *p = NULL, *q = NULL, *dP = NULL, *dQ = NULL, *qInv = NULL; + int p_len, q_len, dP_len, dQ_len, qInv_len; +#endif + + /* not in der format */ + if (buf[0] != ASN1_SEQUENCE) /* basic sanity check */ + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("Error: This is not a valid ASN.1 file\n"); +#endif + return X509_INVALID_PRIV_KEY; + } + + /* Use the private key to mix up the RNG if possible. */ + RNG_custom_init(buf, len); + + mod_len = asn1_get_int(buf, &offset, &modulus); + pub_len = asn1_get_int(buf, &offset, &pub_exp); + priv_len = asn1_get_int(buf, &offset, &priv_exp); + + if (mod_len <= 0 || pub_len <= 0 || priv_len <= 0) + return X509_INVALID_PRIV_KEY; + +#ifdef CONFIG_BIGINT_CRT + p_len = asn1_get_int(buf, &offset, &p); + q_len = asn1_get_int(buf, &offset, &q); + dP_len = asn1_get_int(buf, &offset, &dP); + dQ_len = asn1_get_int(buf, &offset, &dQ); + qInv_len = asn1_get_int(buf, &offset, &qInv); + + if (p_len <= 0 || q_len <= 0 || dP_len <= 0 || dQ_len <= 0 || qInv_len <= 0) + return X509_INVALID_PRIV_KEY; + + RSA_priv_key_new(rsa_ctx, + modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len, + p, p_len, q, p_len, dP, dP_len, dQ, dQ_len, qInv, qInv_len); + + os_free(p); + os_free(q); + os_free(dP); + os_free(dQ); + os_free(qInv); +#else + RSA_priv_key_new(rsa_ctx, + modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len); +#endif + + os_free(modulus); + os_free(priv_exp); + os_free(pub_exp); + return X509_OK; +} + +/** + * Get the time of a certificate. Ignore hours/minutes/seconds. + */ +static int ICACHE_FLASH_ATTR asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t) +{ + int ret = X509_NOT_OK, len, t_offset; + struct tm tm; + + if (buf[(*offset)++] != ASN1_UTC_TIME) + goto end_utc_time; + + len = get_asn1_length(buf, offset); + t_offset = *offset; + + os_memset(&tm, 0, sizeof(struct tm)); + tm.tm_year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0'); + + if (tm.tm_year <= 50) /* 1951-2050 thing */ + { + tm.tm_year += 100; + } + + tm.tm_mon = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0') - 1; + tm.tm_mday = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0'); + +// wujg : pass compile first +// *t = mktime(&tm); + *offset += len; + ret = X509_OK; + +end_utc_time: + return ret; +} + +/** + * Get the version type of a certificate (which we don't actually care about) + */ +int ICACHE_FLASH_ATTR asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK; + + (*offset) += 2; /* get past explicit tag */ + if (asn1_skip_obj(cert, offset, ASN1_INTEGER)) + goto end_version; + + ret = X509_OK; +end_version: + return ret; +} + +/** + * Retrieve the notbefore and notafter certificate times. + */ +int ICACHE_FLASH_ATTR asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + return (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || + asn1_get_utc_time(cert, offset, &x509_ctx->not_before) || + asn1_get_utc_time(cert, offset, &x509_ctx->not_after)); +} + +/** + * Get the components of a distinguished name + */ +static int ICACHE_FLASH_ATTR asn1_get_oid_x520(const uint8_t *buf, int *offset) +{ + int dn_type = 0; + int len; + + if ((len = asn1_next_obj(buf, offset, ASN1_OID)) < 0) + goto end_oid; + + /* expect a sequence of 2.5.4.[x] where x is a one of distinguished name + components we are interested in. */ + if (len == 3 && buf[(*offset)++] == 0x55 && buf[(*offset)++] == 0x04) + dn_type = buf[(*offset)++]; + else + { + *offset += len; /* skip over it */ + } + +end_oid: + return dn_type; +} + +/** + * Obtain an ASN.1 printable string type. + */ +static int ICACHE_FLASH_ATTR asn1_get_printable_str(const uint8_t *buf, int *offset, char **str) +{ + int len = X509_NOT_OK; + int asn1_type = buf[*offset]; + + /* some certs have this awful crud in them for some reason */ + if (asn1_type != ASN1_PRINTABLE_STR && + asn1_type != ASN1_PRINTABLE_STR2 && + asn1_type != ASN1_TELETEX_STR && + asn1_type != ASN1_IA5_STR && + asn1_type != ASN1_UNICODE_STR) + goto end_pnt_str; + + (*offset)++; + len = get_asn1_length(buf, offset); + + if (asn1_type == ASN1_UNICODE_STR) + { + int i; + *str = (char *)os_malloc(len/2+1); /* allow for null */ + + for (i = 0; i < len; i += 2) + (*str)[i/2] = buf[*offset + i + 1]; + + (*str)[len/2] = 0; /* null terminate */ + } + else + { + *str = (char *)os_malloc(len+1); /* allow for null */ + os_memcpy(*str, &buf[*offset], len); + (*str)[len] = 0; /* null terminate */ + } + + *offset += len; + +end_pnt_str: + return len; +} + +/** + * Get the subject name (or the issuer) of a certificate. + */ +int ICACHE_FLASH_ATTR asn1_name(const uint8_t *cert, int *offset, char *dn[]) +{ + int ret = X509_NOT_OK; + int dn_type; + char *tmp; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0) + goto end_name; + + while (asn1_next_obj(cert, offset, ASN1_SET) >= 0) + { + int i, found = 0; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || + (dn_type = asn1_get_oid_x520(cert, offset)) < 0) + goto end_name; + + tmp = NULL; + + if (asn1_get_printable_str(cert, offset, &tmp) < 0) + { + os_free(tmp); + goto end_name; + } + + /* find the distinguished named type */ + for (i = 0; i < X509_NUM_DN_TYPES; i++) + { + if (dn_type == g_dn_types[i]) + { + if (dn[i] == NULL) + { + dn[i] = tmp; + found = 1; + break; + } + } + } + + if (found == 0) /* not found so get rid of it */ + { + os_free(tmp); + } + } + + ret = X509_OK; +end_name: + return ret; +} + +/** + * Read the modulus and public exponent of a certificate. + */ +int ICACHE_FLASH_ATTR asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK, mod_len, pub_len; + uint8_t *modulus = NULL, *pub_exp = NULL; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(cert, offset, ASN1_SEQUENCE) || + asn1_next_obj(cert, offset, ASN1_BIT_STRING) < 0) + goto end_pub_key; + + (*offset)++; /* ignore the padding bit field */ + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0) + goto end_pub_key; + + mod_len = asn1_get_int(cert, offset, &modulus); + pub_len = asn1_get_int(cert, offset, &pub_exp); + + RSA_pub_key_new(&x509_ctx->rsa_ctx, modulus, mod_len, pub_exp, pub_len); + + os_free(modulus); + os_free(pub_exp); + ret = X509_OK; + +end_pub_key: + return ret; +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Read the signature of the certificate. + */ +int ICACHE_FLASH_ATTR asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK; + + if (cert[(*offset)++] != ASN1_BIT_STRING) + goto end_sig; + + x509_ctx->sig_len = get_asn1_length(cert, offset)-1; + (*offset)++; /* ignore bit string padding bits */ + x509_ctx->signature = (uint8_t *)os_malloc(x509_ctx->sig_len); + os_memcpy(x509_ctx->signature, &cert[*offset], x509_ctx->sig_len); + *offset += x509_ctx->sig_len; + ret = X509_OK; + +end_sig: + return ret; +} + +/* + * Compare 2 distinguished name components for equality + * @return 0 if a match + */ +static int ICACHE_FLASH_ATTR asn1_compare_dn_comp(const char *dn1, const char *dn2) +{ + int ret; + + if (dn1 == NULL && dn2 == NULL) + ret = 0; + else + ret = (dn1 && dn2) ? os_strcmp(dn1, dn2) : 1; + + return ret; +} + +/** + * Clean up all of the CA certificates. + */ +void ICACHE_FLASH_ATTR remove_ca_certs(CA_CERT_CTX *ca_cert_ctx) +{ + int i = 0; + + if (ca_cert_ctx == NULL) + return; + + while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) + { + x509_free(ca_cert_ctx->cert[i]); + ca_cert_ctx->cert[i++] = NULL; + } + + os_free(ca_cert_ctx); +} + +/* + * Compare 2 distinguished names for equality + * @return 0 if a match + */ +int ICACHE_FLASH_ATTR asn1_compare_dn(char * const dn1[], char * const dn2[]) +{ + int i; + + for (i = 0; i < X509_NUM_DN_TYPES; i++) + { + if (asn1_compare_dn_comp(dn1[i], dn2[i])) + return 1; + } + + return 0; /* all good */ +} + +int ICACHE_FLASH_ATTR asn1_find_oid(const uint8_t* cert, int* offset, + const uint8_t* oid, int oid_length) +{ + int seqlen; + if ((seqlen = asn1_next_obj(cert, offset, ASN1_SEQUENCE))> 0) + { + int end = *offset + seqlen; + + while (*offset < end) + { + int type = cert[(*offset)++]; + int length = get_asn1_length(cert, offset); + int noffset = *offset + length; + + if (type == ASN1_SEQUENCE) + { + type = cert[(*offset)++]; + length = get_asn1_length(cert, offset); + + if (type == ASN1_OID && length == oid_length && + os_memcmp(cert + *offset, oid, oid_length) == 0) + { + *offset += oid_length; + return 1; + } + } + + *offset = noffset; + } + } + + return 0; +} + +int ICACHE_FLASH_ATTR asn1_find_subjectaltname(const uint8_t* cert, int offset) +{ + if (asn1_find_oid(cert, &offset, sig_subject_alt_name, + SIG_SUBJECT_ALT_NAME_SIZE)) + { + return offset; + } + + return 0; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ + +/** + * Read the signature type of the certificate. We only support RSA-MD5 and + * RSA-SHA1 signature types. + */ +int ICACHE_FLASH_ATTR asn1_signature_type(const uint8_t *cert, + int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK, len; + + if (cert[(*offset)++] != ASN1_OID) + goto end_check_sig; + + len = get_asn1_length(cert, offset); + + if (len == 5 && os_memcmp(sig_sha1WithRSAEncrypt, &cert[*offset], + SIG_IIS6_OID_SIZE) == 0) + { + x509_ctx->sig_type = SIG_TYPE_SHA1; + } + else + { + if (os_memcmp(sig_oid_prefix, &cert[*offset], SIG_OID_PREFIX_SIZE)) + goto end_check_sig; /* unrecognised cert type */ + + x509_ctx->sig_type = cert[*offset + SIG_OID_PREFIX_SIZE]; + } + + *offset += len; + asn1_skip_obj(cert, offset, ASN1_NULL); /* if it's there */ + ret = X509_OK; + +end_check_sig: + return ret; +} + diff --git a/app/ssl/ssl/ssl_gen_cert.c b/app/ssl/ssl/ssl_gen_cert.c new file mode 100644 index 00000000..80f14d23 --- /dev/null +++ b/app/ssl/ssl/ssl_gen_cert.c @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ssl/ssl_config.h" + +#ifdef CONFIG_SSL_GENERATE_X509_CERT +#include +#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_ssl.h" + +/** + * Generate a basic X.509 certificate + */ + +static uint8_t ICACHE_FLASH_ATTR set_gen_length(int len, uint8_t *buf, int *offset) +{ + if (len < 0x80) /* short form */ + { + buf[(*offset)++] = len; + return 1; + } + else /* long form */ + { + int i, length_bytes = 0; + + if (len & 0x00FF0000) + length_bytes = 3; + else if (len & 0x0000FF00) + length_bytes = 2; + else if (len & 0x000000FF) + length_bytes = 1; + + buf[(*offset)++] = 0x80 + length_bytes; + + for (i = length_bytes-1; i >= 0; i--) + { + buf[*offset+i] = len & 0xFF; + len >>= 8; + } + + *offset += length_bytes; + return length_bytes+1; + } +} + +static int ICACHE_FLASH_ATTR pre_adjust_with_size(uint8_t type, + int *seq_offset, uint8_t *buf, int *offset) +{ + buf[(*offset)++] = type; + *seq_offset = *offset; + *offset += 4; /* fill in later */ + return *offset; +} + +static void ICACHE_FLASH_ATTR adjust_with_size(int seq_size, int seq_start, + uint8_t *buf, int *offset) +{ + uint8_t seq_byte_size; + int orig_seq_size = seq_size; + int orig_seq_start = seq_start; + + seq_size = *offset-seq_size; + seq_byte_size = set_gen_length(seq_size, buf, &seq_start); + + if (seq_byte_size != 4) + { + memmove(&buf[orig_seq_start+seq_byte_size], + &buf[orig_seq_size], seq_size); + *offset -= 4-seq_byte_size; + } +} + +static void ICACHE_FLASH_ATTR gen_serial_number(uint8_t *buf, int *offset) +{ + static const uint8_t ser_oid[] = { ASN1_INTEGER, 1, 0x7F }; + memcpy(&buf[*offset], ser_oid , sizeof(ser_oid)); + *offset += sizeof(ser_oid); +} + +static void ICACHE_FLASH_ATTR gen_signature_alg(uint8_t *buf, int *offset) +{ + /* OBJECT IDENTIFIER sha1withRSAEncryption (1 2 840 113549 1 1 5) */ + static const uint8_t sig_oid[] = + { + ASN1_SEQUENCE, 0x0d, ASN1_OID, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + ASN1_NULL, 0x00 + }; + + memcpy(&buf[*offset], sig_oid, sizeof(sig_oid)); + *offset += sizeof(sig_oid); +} + +static int ICACHE_FLASH_ATTR gen_dn(const char *name, uint8_t dn_type, + uint8_t *buf, int *offset) +{ + int ret = X509_OK; + int name_size = strlen(name); + + if (name_size > 0x70) /* just too big */ + { + ret = X509_NOT_OK; + goto error; + } + + buf[(*offset)++] = ASN1_SET; + set_gen_length(9+name_size, buf, offset); + buf[(*offset)++] = ASN1_SEQUENCE; + set_gen_length(7+name_size, buf, offset); + buf[(*offset)++] = ASN1_OID; + buf[(*offset)++] = 3; + buf[(*offset)++] = 0x55; + buf[(*offset)++] = 0x04; + buf[(*offset)++] = dn_type; + buf[(*offset)++] = ASN1_PRINTABLE_STR; + buf[(*offset)++] = name_size; + strcpy(&buf[*offset], name); + *offset += name_size; + +error: + return ret; +} + +static int ICACHE_FLASH_ATTR gen_issuer(const char * dn[], uint8_t *buf, int *offset) +{ + int ret = X509_OK; + int seq_offset; + int seq_size = pre_adjust_with_size( + ASN1_SEQUENCE, &seq_offset, buf, offset); + char fqdn[128]; + + /* we need the common name, so if not configured, work out the fully + * qualified domain name */ + if (dn[X509_COMMON_NAME] == NULL || strlen(dn[X509_COMMON_NAME]) == 0) + { + int fqdn_len; + gethostname(fqdn, sizeof(fqdn)); + fqdn_len = strlen(fqdn); + fqdn[fqdn_len++] = '.'; + getdomainname(&fqdn[fqdn_len], sizeof(fqdn)-fqdn_len); + fqdn_len = strlen(fqdn); + + if (fqdn[fqdn_len-1] == '.') /* ensure '.' is not last char */ + fqdn[fqdn_len-1] = 0; + + dn[X509_COMMON_NAME] = fqdn; + } + + if ((ret = gen_dn(dn[X509_COMMON_NAME], 3, buf, offset))) + goto error; + + if (dn[X509_ORGANIZATION] != NULL && strlen(dn[X509_ORGANIZATION]) > 0) + { + if ((ret = gen_dn(dn[X509_ORGANIZATION], 10, buf, offset))) + goto error; + } + + if (dn[X509_ORGANIZATIONAL_UNIT] != NULL && + strlen(dn[X509_ORGANIZATIONAL_UNIT]) > 0) + { + if ((ret = gen_dn(dn[X509_ORGANIZATIONAL_UNIT], 11, buf, offset))) + goto error; + } + + adjust_with_size(seq_size, seq_offset, buf, offset); + +error: + return ret; +} + +static void ICACHE_FLASH_ATTR gen_utc_time(uint8_t *buf, int *offset) +{ + static const uint8_t time_seq[] = + { + ASN1_SEQUENCE, 30, + ASN1_UTC_TIME, 13, + '0', '7', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z', + ASN1_UTC_TIME, 13, /* make it good for 30 or so years */ + '3', '8', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z' + }; + + /* fixed time */ + memcpy(&buf[*offset], time_seq, sizeof(time_seq)); + *offset += sizeof(time_seq); +} + +static void ICACHE_FLASH_ATTR gen_pub_key2(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) +{ + static const uint8_t pub_key_seq[] = + { + ASN1_INTEGER, 0x03, 0x01, 0x00, 0x01 /* INTEGER 65537 */ + }; + + int seq_offset; + int pub_key_size = rsa_ctx->num_octets; + uint8_t *block = (uint8_t *)alloca(pub_key_size); + int seq_size = pre_adjust_with_size( + ASN1_SEQUENCE, &seq_offset, buf, offset); + buf[(*offset)++] = ASN1_INTEGER; + bi_export(rsa_ctx->bi_ctx, rsa_ctx->m, block, pub_key_size); + + if (*block & 0x80) /* make integer positive */ + { + set_gen_length(pub_key_size+1, buf, offset); + buf[(*offset)++] = 0; + } + else + set_gen_length(pub_key_size, buf, offset); + + memcpy(&buf[*offset], block, pub_key_size); + *offset += pub_key_size; + memcpy(&buf[*offset], pub_key_seq, sizeof(pub_key_seq)); + *offset += sizeof(pub_key_seq); + adjust_with_size(seq_size, seq_offset, buf, offset); +} + +static void ICACHE_FLASH_ATTR gen_pub_key1(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) +{ + int seq_offset; + int seq_size = pre_adjust_with_size( + ASN1_BIT_STRING, &seq_offset, buf, offset); + buf[(*offset)++] = 0; /* bit string is multiple of 8 */ + gen_pub_key2(rsa_ctx, buf, offset); + adjust_with_size(seq_size, seq_offset, buf, offset); +} + +static void ICACHE_FLASH_ATTR gen_pub_key(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) +{ + /* OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) */ + static const uint8_t rsa_enc_oid[] = + { + ASN1_SEQUENCE, 0x0d, ASN1_OID, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + ASN1_NULL, 0x00 + }; + + int seq_offset; + int seq_size = pre_adjust_with_size( + ASN1_SEQUENCE, &seq_offset, buf, offset); + + memcpy(&buf[*offset], rsa_enc_oid, sizeof(rsa_enc_oid)); + *offset += sizeof(rsa_enc_oid); + gen_pub_key1(rsa_ctx, buf, offset); + adjust_with_size(seq_size, seq_offset, buf, offset); +} + +static void ICACHE_FLASH_ATTR gen_signature(const RSA_CTX *rsa_ctx, const uint8_t *sha_dgst, + uint8_t *buf, int *offset) +{ + static const uint8_t asn1_sig[] = + { + ASN1_SEQUENCE, 0x21, ASN1_SEQUENCE, 0x09, ASN1_OID, 0x05, + 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* sha1 (1 3 14 3 2 26) */ + ASN1_NULL, 0x00, ASN1_OCTET_STRING, 0x14 + }; + + uint8_t *enc_block = (uint8_t *)alloca(rsa_ctx->num_octets); + uint8_t *block = (uint8_t *)alloca(sizeof(asn1_sig) + SHA1_SIZE); + int sig_size; + + /* add the digest as an embedded asn.1 sequence */ + memcpy(block, asn1_sig, sizeof(asn1_sig)); + memcpy(&block[sizeof(asn1_sig)], sha_dgst, SHA1_SIZE); + + sig_size = RSA_encrypt(rsa_ctx, block, + sizeof(asn1_sig) + SHA1_SIZE, enc_block, 1); + + buf[(*offset)++] = ASN1_BIT_STRING; + set_gen_length(sig_size+1, buf, offset); + buf[(*offset)++] = 0; /* bit string is multiple of 8 */ + memcpy(&buf[*offset], enc_block, sig_size); + *offset += sig_size; +} + +static int ICACHE_FLASH_ATTR gen_tbs_cert(const char * dn[], + const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset, + uint8_t *sha_dgst) +{ + int ret = X509_OK; + SHA1_CTX sha_ctx; + int seq_offset; + int begin_tbs = *offset; + int seq_size = pre_adjust_with_size( + ASN1_SEQUENCE, &seq_offset, buf, offset); + + gen_serial_number(buf, offset); + gen_signature_alg(buf, offset); + + /* CA certicate issuer */ + if ((ret = gen_issuer(dn, buf, offset))) + goto error; + + gen_utc_time(buf, offset); + + /* certificate issuer */ + if ((ret = gen_issuer(dn, buf, offset))) + goto error; + + gen_pub_key(rsa_ctx, buf, offset); + adjust_with_size(seq_size, seq_offset, buf, offset); + + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, &buf[begin_tbs], *offset-begin_tbs); + SHA1_Final(sha_dgst, &sha_ctx); + +error: + return ret; +} + +/** + * Create a new certificate. + */ +EXP_FUNC int ICACHE_FLASH_ATTR STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, uint32_t options, const char * dn[], uint8_t **cert_data) +{ + int ret = X509_OK, offset = 0, seq_offset; + /* allocate enough space to load a new certificate */ + uint8_t *buf = (uint8_t *)alloca(ssl_ctx->rsa_ctx->num_octets*2 + 512); + uint8_t sha_dgst[SHA1_SIZE]; + int seq_size = pre_adjust_with_size(ASN1_SEQUENCE, + &seq_offset, buf, &offset); + + if ((ret = gen_tbs_cert(dn, ssl_ctx->rsa_ctx, buf, &offset, sha_dgst)) < 0) + goto error; + + gen_signature_alg(buf, &offset); + gen_signature(ssl_ctx->rsa_ctx, sha_dgst, buf, &offset); + adjust_with_size(seq_size, seq_offset, buf, &offset); + *cert_data = (uint8_t *)os_malloc(offset); /* create the exact memory for it */ + memcpy(*cert_data, buf, offset); + +error: + return ret < 0 ? ret : offset; +} + +#endif + diff --git a/app/ssl/ssl/ssl_loader.c b/app/ssl/ssl/ssl_loader.c new file mode 100644 index 00000000..044faac4 --- /dev/null +++ b/app/ssl/ssl/ssl_loader.c @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Load certificates/keys into memory. These can be in many different formats. + * PEM support and other formats can be processed here. + * + * The PEM private keys may be optionally encrypted with AES128 or AES256. + * The encrypted PEM keys were generated with something like: + * + * openssl genrsa -aes128 -passout pass:abcd -out axTLS.key_aes128.pem 512 + */ + +//#include +//#include +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_ssl.h" + +static int do_obj(SSL_CTX *ssl_ctx, int obj_type, + SSLObjLoader *ssl_obj, const char *password); +#ifdef CONFIG_SSL_HAS_PEM +static int ssl_obj_PEM_load(SSL_CTX *ssl_ctx, int obj_type, + SSLObjLoader *ssl_obj, const char *password); +#endif + +/* + * Load a file into memory that is in binary DER (or ascii PEM) format. + */ +EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type, + const char *filename, const char *password) +{ +#ifndef CONFIG_SSL_SKELETON_MODE + static const char * const begin = "-----BEGIN"; + int ret = SSL_OK; + SSLObjLoader *ssl_obj = NULL; + + if (filename == NULL) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + + ssl_obj = (SSLObjLoader *)os_zalloc(sizeof(SSLObjLoader)); + ssl_obj->len = get_file(filename, &ssl_obj->buf); + if (ssl_obj->len <= 0) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + + /* is the file a PEM file? */ + if ((char *)os_strstr((const char *)ssl_obj->buf, begin) != NULL) + { +#ifdef CONFIG_SSL_HAS_PEM + ret = ssl_obj_PEM_load(ssl_ctx, obj_type, ssl_obj, password); +#else + ssl_printf(unsupported_str); + ret = SSL_ERROR_NOT_SUPPORTED; +#endif + } + else + ret = do_obj(ssl_ctx, obj_type, ssl_obj, password); + +error: + ssl_obj_free(ssl_obj); + return ret; +#else + ssl_printf(unsupported_str); + return SSL_ERROR_NOT_SUPPORTED; +#endif /* CONFIG_SSL_SKELETON_MODE */ +} + +/* + * Transfer binary data into the object loader. + */ +EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_obj_memory_load(SSL_CTX *ssl_ctx, int mem_type, + const uint8_t *data, int len, const char *password) +{ + int ret; + SSLObjLoader *ssl_obj; + + ssl_obj = (SSLObjLoader *)os_zalloc(sizeof(SSLObjLoader)); + ssl_obj->buf = (uint8_t *)os_malloc(len); + os_memcpy(ssl_obj->buf, data, len); + ssl_obj->len = len; + ret = do_obj(ssl_ctx, mem_type, ssl_obj, password); + ssl_obj_free(ssl_obj); + return ret; +} + +/* + * Actually work out what we are doing + */ +static int ICACHE_FLASH_ATTR do_obj(SSL_CTX *ssl_ctx, int obj_type, + SSLObjLoader *ssl_obj, const char *password) +{ + int ret = SSL_OK; + + switch (obj_type) + { + case SSL_OBJ_RSA_KEY: + ret = add_private_key(ssl_ctx, ssl_obj); + break; + + case SSL_OBJ_X509_CERT: + ret = add_cert(ssl_ctx, ssl_obj->buf, ssl_obj->len); + break; + +#ifdef CONFIG_SSL_CERT_VERIFICATION + case SSL_OBJ_X509_CACERT: + add_cert_auth(ssl_ctx, ssl_obj->buf, ssl_obj->len); + break; +#endif + +#ifdef CONFIG_SSL_USE_PKCS12 + case SSL_OBJ_PKCS8: + ret = pkcs8_decode(ssl_ctx, ssl_obj, password); + break; + + case SSL_OBJ_PKCS12: + ret = pkcs12_decode(ssl_ctx, ssl_obj, password); + break; +#endif + default: + ssl_printf(unsupported_str); + ret = SSL_ERROR_NOT_SUPPORTED; + break; + } + + return ret; +} + +/* + * Clean up our mess. + */ +void ICACHE_FLASH_ATTR ssl_obj_free(SSLObjLoader *ssl_obj) +{ + if (ssl_obj) + { + os_free(ssl_obj->buf); + os_free(ssl_obj); + } +} + +/* + * Support for PEM encoded keys/certificates. + */ +#ifdef CONFIG_SSL_HAS_PEM + +#define NUM_PEM_TYPES 4 +#define IV_SIZE 16 +#define IS_RSA_PRIVATE_KEY 0 +#define IS_ENCRYPTED_PRIVATE_KEY 1 +#define IS_PRIVATE_KEY 2 +#define IS_CERTIFICATE 3 + +static const char * const begins[NUM_PEM_TYPES] = +{ + "-----BEGIN RSA PRIVATE KEY-----", + "-----BEGIN ENCRYPTED PRIVATE KEY-----", + "-----BEGIN PRIVATE KEY-----", + "-----BEGIN CERTIFICATE-----", +}; + +static const char * const ends[NUM_PEM_TYPES] = +{ + "-----END RSA PRIVATE KEY-----", + "-----END ENCRYPTED PRIVATE KEY-----", + "-----END PRIVATE KEY-----", + "-----END CERTIFICATE-----", +}; + +static const char * const aes_str[2] = +{ + "DEK-Info: AES-128-CBC,", + "DEK-Info: AES-256-CBC," +}; + +/** + * Take a base64 blob of data and decrypt it (using AES) into its + * proper ASN.1 form. + */ +static int ICACHE_FLASH_ATTR pem_decrypt(const char *where, const char *end, + const char *password, SSLObjLoader *ssl_obj) +{ + int ret = -1; + int is_aes_256 = 0; + char *start = NULL; + uint8_t iv[IV_SIZE]; + int i, pem_size; + MD5_CTX md5_ctx; + AES_CTX aes_ctx; + uint8_t key[32]; /* AES256 size */ + + if (password == NULL || os_strlen(password) == 0) + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("Error: Need a password for this PEM file\n"); //TTY_FLUSH(); +#endif + goto error; + } + + if ((start = (char *)os_strstr((const char *)where, aes_str[0]))) /* AES128? */ + { + start += os_strlen(aes_str[0]); + } + else if ((start = (char *)os_strstr((const char *)where, aes_str[1]))) /* AES256? */ + { + is_aes_256 = 1; + start += os_strlen(aes_str[1]); + } + else + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("Error: Unsupported password cipher\n"); //TTY_FLUSH(); +#endif + goto error; + } + + /* convert from hex to binary - assumes uppercase hex */ + for (i = 0; i < IV_SIZE; i++) + { + char c = *start++ - '0'; + iv[i] = (c > 9 ? c + '0' - 'A' + 10 : c) << 4; + c = *start++ - '0'; + iv[i] += (c > 9 ? c + '0' - 'A' + 10 : c); + } + + while (*start == '\r' || *start == '\n') + start++; + + /* turn base64 into binary */ + pem_size = (int)(end-start); + if (base64_decode(start, pem_size, ssl_obj->buf, &ssl_obj->len) != 0) + goto error; + + /* work out the key */ + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, (const uint8_t *)password, os_strlen(password)); + MD5_Update(&md5_ctx, iv, SALT_SIZE); + MD5_Final(key, &md5_ctx); + + if (is_aes_256) + { + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, key, MD5_SIZE); + MD5_Update(&md5_ctx, (const uint8_t *)password, os_strlen(password)); + MD5_Update(&md5_ctx, iv, SALT_SIZE); + MD5_Final(&key[MD5_SIZE], &md5_ctx); + } + + /* decrypt using the key/iv */ + AES_set_key(&aes_ctx, key, iv, is_aes_256 ? AES_MODE_256 : AES_MODE_128); + AES_convert_key(&aes_ctx); + AES_cbc_decrypt(&aes_ctx, ssl_obj->buf, ssl_obj->buf, ssl_obj->len); + ret = 0; + +error: + return ret; +} + +/** + * Take a base64 blob of data and turn it into its proper ASN.1 form. + */ +static int ICACHE_FLASH_ATTR new_pem_obj(SSL_CTX *ssl_ctx, int is_cacert, char *where, + int remain, const char *password) +{ + int ret = SSL_ERROR_BAD_CERTIFICATE; + SSLObjLoader *ssl_obj = NULL; + + while (remain > 0) + { + int i, pem_size, obj_type; + char *start = NULL, *end = NULL; + + for (i = 0; i < NUM_PEM_TYPES; i++) + { + if ((start = (char *)os_strstr(where, begins[i])) && + (end = (char *)os_strstr(where, ends[i]))) + { + remain -= (int)(end-where); + start += os_strlen(begins[i]); + pem_size = (int)(end-start); + + ssl_obj = (SSLObjLoader *)os_zalloc(sizeof(SSLObjLoader)); + + /* 4/3 bigger than what we need but so what */ + ssl_obj->buf = (uint8_t *)os_zalloc(pem_size); + ssl_obj->len = pem_size; + + if (i == IS_RSA_PRIVATE_KEY && + os_strstr(start, "Proc-Type:") && + os_strstr(start, "4,ENCRYPTED")) + { + /* check for encrypted PEM file */ + if (pem_decrypt(start, end, password, ssl_obj) < 0) + { + ret = SSL_ERROR_BAD_CERTIFICATE; + goto error; + } + } + else + { + ssl_obj->len = pem_size; + if (base64_decode(start, pem_size, + ssl_obj->buf, &ssl_obj->len) != 0) + { + ret = SSL_ERROR_BAD_CERTIFICATE; + goto error; + } + } + + switch (i) + { + case IS_RSA_PRIVATE_KEY: + obj_type = SSL_OBJ_RSA_KEY; + break; + + case IS_ENCRYPTED_PRIVATE_KEY: + case IS_PRIVATE_KEY: + obj_type = SSL_OBJ_PKCS8; + break; + + case IS_CERTIFICATE: + obj_type = is_cacert ? + SSL_OBJ_X509_CACERT : SSL_OBJ_X509_CERT; + break; + + default: + ret = SSL_ERROR_BAD_CERTIFICATE; + goto error; + } + + /* In a format we can now understand - so process it */ + if ((ret = do_obj(ssl_ctx, obj_type, ssl_obj, password))) + goto error; + + end += os_strlen(ends[i]); + remain -= os_strlen(ends[i]); + while (remain > 0 && (*end == '\r' || *end == '\n')) + { + end++; + remain--; + } + + where = end; + break; + } + } + + ssl_obj_free(ssl_obj); + ssl_obj = NULL; + if (start == NULL) + break; + } +error: + ssl_obj_free(ssl_obj); + return ret; +} + +/* + * Load a file into memory that is in ASCII PEM format. + */ +static int ICACHE_FLASH_ATTR ssl_obj_PEM_load(SSL_CTX *ssl_ctx, int obj_type, + SSLObjLoader *ssl_obj, const char *password) +{ + char *start; + + /* add a null terminator */ + ssl_obj->len++; + ssl_obj->buf = (uint8_t *)os_realloc(ssl_obj->buf, ssl_obj->len); + ssl_obj->buf[ssl_obj->len-1] = 0; + start = (char *)ssl_obj->buf; + return new_pem_obj(ssl_ctx, obj_type == SSL_OBJ_X509_CACERT, + start, ssl_obj->len, password); +} +#endif /* CONFIG_SSL_HAS_PEM */ + +/** + * Load the key/certificates in memory depending on compile-time and user + * options. + */ +int ICACHE_FLASH_ATTR load_key_certs(SSL_CTX *ssl_ctx) +{ + int ret = SSL_OK; + uint32_t options = ssl_ctx->options; +#ifdef CONFIG_SSL_GENERATE_X509_CERT + uint8_t *cert_data = NULL; + int cert_size; + static const char *dn[] = + { + CONFIG_SSL_X509_COMMON_NAME, + CONFIG_SSL_X509_ORGANIZATION_NAME, + CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME + }; +#endif + + /* do the private key first */ + if (os_strlen(CONFIG_SSL_PRIVATE_KEY_LOCATION) > 0) + { + if ((ret = ssl_obj_load(ssl_ctx, SSL_OBJ_RSA_KEY, + CONFIG_SSL_PRIVATE_KEY_LOCATION, + CONFIG_SSL_PRIVATE_KEY_PASSWORD)) < 0) + goto error; + } + else if (!(options & SSL_NO_DEFAULT_KEY)) + { +#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE) + // static const /* saves a few more bytes */ +//#include "private_key.h" + + extern unsigned int default_private_key_len; + extern unsigned char default_private_key[]; + ssl_obj_memory_load(ssl_ctx, SSL_OBJ_RSA_KEY, default_private_key, + default_private_key_len, NULL); +#endif + } + + /* now load the certificate */ +#ifdef CONFIG_SSL_GENERATE_X509_CERT + if ((cert_size = ssl_x509_create(ssl_ctx, 0, dn, &cert_data)) < 0) + { + ret = cert_size; + goto error; + } + + ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT, cert_data, cert_size, NULL); + os_free(cert_data); +#else + if (os_strlen(CONFIG_SSL_X509_CERT_LOCATION)) + { + if ((ret = ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, + CONFIG_SSL_X509_CERT_LOCATION, NULL)) < 0) + goto error; + } + else if (!(options & SSL_NO_DEFAULT_KEY)) + { +#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE) +// static const /* saves a few bytes and RAM */ +//#include "cert.h" + extern unsigned char default_certificate[]; + extern unsigned int default_certificate_len; + ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT, + default_certificate, default_certificate_len, NULL); +#endif + } +#endif + +error: +#ifdef CONFIG_SSL_FULL_MODE + if (ret) + { + ssl_printf("Error: Certificate or key not loaded\n"); //TTY_FLUSH(); + } +#endif + + return ret; + +} diff --git a/app/ssl/ssl/ssl_openssl.c b/app/ssl/ssl/ssl_openssl.c new file mode 100644 index 00000000..b77f2ed5 --- /dev/null +++ b/app/ssl/ssl/ssl_openssl.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Enable a subset of openssl compatible functions. We don't aim to be 100% + * compatible - just to be able to do basic ports etc. + * + * Only really tested on mini_httpd, so I'm not too sure how extensive this + * port is. + */ + +#include "ssl/ssl_config.h" + +#ifdef CONFIG_OPENSSL_COMPATIBLE +#include +#include +#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_ssl.h" + +#define OPENSSL_CTX_ATTR ((OPENSSL_CTX *)ssl_ctx->bonus_attr) + +static char *key_password = NULL; + +void *SSLv23_server_method(void) { return NULL; } +void *SSLv3_server_method(void) { return NULL; } +void *TLSv1_server_method(void) { return NULL; } +void *SSLv23_client_method(void) { return NULL; } +void *SSLv3_client_method(void) { return NULL; } +void *TLSv1_client_method(void) { return NULL; } + +typedef void * (*ssl_func_type_t)(void); +typedef void * (*bio_func_type_t)(void); + +typedef struct +{ + ssl_func_type_t ssl_func_type; +} OPENSSL_CTX; + +SSL_CTX *ICACHE_FLASH_ATTR SSL_CTX_new(ssl_func_type_t meth) +{ + SSL_CTX *ssl_ctx = ssl_ctx_new(0, 5); + ssl_ctx->bonus_attr = os_malloc(sizeof(OPENSSL_CTX)); + OPENSSL_CTX_ATTR->ssl_func_type = meth; + return ssl_ctx; +} + +void ICACHE_FLASH_ATTR SSL_CTX_free(SSL_CTX *ssl_ctx) +{ + free(ssl_ctx->bonus_attr); + ssl_ctx_free(ssl_ctx); +} + +SSL *ICACHE_FLASH_ATTR SSL_new(SSL_CTX *ssl_ctx) +{ + SSL *ssl; + ssl_func_type_t ssl_func_type; + + ssl = ssl_new(ssl_ctx, -1); /* fd is set later */ + ssl_func_type = OPENSSL_CTX_ATTR->ssl_func_type; + +#ifdef CONFIG_SSL_ENABLE_CLIENT + if (ssl_func_type == SSLv23_client_method || + ssl_func_type == SSLv3_client_method || + ssl_func_type == TLSv1_client_method) + { + SET_SSL_FLAG(SSL_IS_CLIENT); + } + else +#endif + { + ssl->next_state = HS_CLIENT_HELLO; + } + + return ssl; +} + +int ICACHE_FLASH_ATTR SSL_set_fd(SSL *s, int fd) +{ + s->client_fd = fd; + return 1; /* always succeeds */ +} + +int ICACHE_FLASH_ATTR SSL_accept(SSL *ssl) +{ + while (ssl_read(ssl, NULL) == SSL_OK) + { + if (ssl->next_state == HS_CLIENT_HELLO) + return 1; /* we're done */ + } + + return -1; +} + +#ifdef CONFIG_SSL_ENABLE_CLIENT +int ICACHE_FLASH_ATTR SSL_connect(SSL *ssl) +{ + return do_client_connect(ssl) == SSL_OK ? 1 : -1; +} +#endif + +void ICACHE_FLASH_ATTR SSL_free(SSL *ssl) +{ + ssl_free(ssl); +} + +int ICACHE_FLASH_ATTR SSL_read(SSL *ssl, void *buf, int num) +{ + uint8_t *read_buf; + int ret; + + while ((ret = ssl_read(ssl, &read_buf)) == SSL_OK); + + if (ret > SSL_OK) + { + os_memcpy(buf, read_buf, ret > num ? num : ret); + } + + return ret; +} + +int ICACHE_FLASH_ATTR SSL_write(SSL *ssl, const void *buf, int num) +{ + return ssl_write(ssl, buf, num); +} + +int ICACHE_FLASH_ATTR SSL_CTX_use_certificate_file(SSL_CTX *ssl_ctx, const char *file, int type) +{ + return (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, file, NULL) == SSL_OK); +} + +int ICACHE_FLASH_ATTR SSL_CTX_use_PrivateKey_file(SSL_CTX *ssl_ctx, const char *file, int type) +{ + return (ssl_obj_load(ssl_ctx, SSL_OBJ_RSA_KEY, file, key_password) == SSL_OK); +} + +int ICACHE_FLASH_ATTR SSL_CTX_use_certificate_ASN1(SSL_CTX *ssl_ctx, int len, const uint8_t *d) +{ + return (ssl_obj_memory_load(ssl_ctx, + SSL_OBJ_X509_CERT, d, len, NULL) == SSL_OK); +} + +int ICACHE_FLASH_ATTR SSL_CTX_set_session_id_context(SSL_CTX *ctx, const unsigned char *sid_ctx, + unsigned int sid_ctx_len) +{ + return 1; +} + +int ICACHE_FLASH_ATTR SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) +{ + return 1; +} + +int ICACHE_FLASH_ATTR SSL_CTX_use_certificate_chain_file(SSL_CTX *ssl_ctx, const char *file) +{ + return (ssl_obj_load(ssl_ctx, + SSL_OBJ_X509_CERT, file, NULL) == SSL_OK); +} + +int ICACHE_FLASH_ATTR SSL_shutdown(SSL *ssl) +{ + return 1; +} + +/*** get/set session ***/ +SSL_SESSION *ICACHE_FLASH_ATTR SSL_get1_session(SSL *ssl) +{ + return (SSL_SESSION *)ssl_get_session_id(ssl); /* note: wrong cast */ +} + +int ICACHE_FLASH_ATTR SSL_set_session(SSL *ssl, SSL_SESSION *session) +{ + os_memcpy(ssl->session_id, (uint8_t *)session, SSL_SESSION_ID_SIZE); + return 1; +} + +void ICACHE_FLASH_ATTR SSL_SESSION_free(SSL_SESSION *session) { } +/*** end get/set session ***/ + +long ICACHE_FLASH_ATTR SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) +{ + return 0; +} + +void ICACHE_FLASH_ATTR SSL_CTX_set_verify(SSL_CTX *ctx, int mode, + int (*verify_callback)(int, void *)) { } + +void ICACHE_FLASH_ATTR SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth) { } + +int ICACHE_FLASH_ATTR SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, + const char *CApath) +{ + return 1; +} + +void *ICACHE_FLASH_ATTR SSL_load_client_CA_file(const char *file) +{ + return (void *)file; +} + +void ICACHE_FLASH_ATTR SSL_CTX_set_client_CA_list(SSL_CTX *ssl_ctx, void *file) +{ + ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, (const char *)file, NULL); +} + +void ICACHE_FLASH_ATTR SSLv23_method(void) { } + +void ICACHE_FLASH_ATTR SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, void *cb) { } + +void ICACHE_FLASH_ATTR SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u) +{ + key_password = (char *)u; +} + +int ICACHE_FLASH_ATTR SSL_peek(SSL *ssl, void *buf, int num) +{ + os_memcpy(buf, ssl->bm_data, num); + return num; +} + +void ICACHE_FLASH_ATTR SSL_set_bio(SSL *ssl, void *rbio, void *wbio) { } + +long ICACHE_FLASH_ATTR SSL_get_verify_result(const SSL *ssl) +{ + return ssl_handshake_status(ssl); +} + +int ICACHE_FLASH_ATTR SSL_state(SSL *ssl) +{ + return 0x03; // ok state +} + +/** end of could do better list */ + +void *ICACHE_FLASH_ATTR SSL_get_peer_certificate(const SSL *ssl) +{ + return &ssl->ssl_ctx->certs[0]; +} + +int ICACHE_FLASH_ATTR SSL_clear(SSL *ssl) +{ + return 1; +} + + +int ICACHE_FLASH_ATTR SSL_CTX_check_private_key(const SSL_CTX *ctx) +{ + return 1; +} + +int ICACHE_FLASH_ATTR SSL_CTX_set_cipher_list(SSL *s, const char *str) +{ + return 1; +} + +int ICACHE_FLASH_ATTR SSL_get_error(const SSL *ssl, int ret) +{ + ssl_display_error(ret); + return 0; /* TODO: return proper return code */ +} + +void ICACHE_FLASH_ATTR SSL_CTX_set_options(SSL_CTX *ssl_ctx, int option) {} +int ICACHE_FLASH_ATTR SSL_library_init(void ) { return 1; } +void ICACHE_FLASH_ATTR SSL_load_error_strings(void ) {} +void ICACHE_FLASH_ATTR ERR_print_errors_fp(FILE *fp) {} + +#ifndef CONFIG_SSL_SKELETON_MODE +long ICACHE_FLASH_ATTR SSL_CTX_get_timeout(const SSL_CTX *ssl_ctx) { + return CONFIG_SSL_EXPIRY_TIME*3600; } +long ICACHE_FLASH_ATTR SSL_CTX_set_timeout(SSL_CTX *ssl_ctx, long t) { + return SSL_CTX_get_timeout(ssl_ctx); } +#endif +void ICACHE_FLASH_ATTR BIO_printf(FILE *f, const char *format, ...) +{ + va_list(ap); + va_start(ap, format); + vfprintf(f, format, ap); + va_end(ap); +} + +void* ICACHE_FLASH_ATTR BIO_s_null(void) { return NULL; } +FILE *ICACHE_FLASH_ATTR BIO_new(bio_func_type_t func) +{ + if (func == BIO_s_null) + return fopen("/dev/null", "r"); + else + return NULL; +} + +FILE *ICACHE_FLASH_ATTR BIO_new_fp(FILE *stream, int close_flag) { return stream; } +int ICACHE_FLASH_ATTR BIO_free(FILE *a) { if (a != stdout && a != stderr) fclose(a); return 1; } + + + +#endif diff --git a/app/ssl/ssl/ssl_os_port.c b/app/ssl/ssl/ssl_os_port.c new file mode 100644 index 00000000..9f9be57e --- /dev/null +++ b/app/ssl/ssl/ssl_os_port.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file os_port.c + * + * OS specific functions. + */ +//#include +//#include +//#include +//#include +#include "ssl/ssl_os_port.h" + +#ifdef WIN32 +/** + * gettimeofday() not in Win32 + */ +EXP_FUNC void STDCALL gettimeofday(struct timeval* t, void* timezone) +{ +#if defined(_WIN32_WCE) + t->tv_sec = time(NULL); + t->tv_usec = 0; /* 1sec precision only */ +#else + struct _timeb timebuffer; + _ftime(&timebuffer); + t->tv_sec = (long)timebuffer.time; + t->tv_usec = 1000 * timebuffer.millitm; /* 1ms precision */ +#endif +} + +/** + * strcasecmp() not in Win32 + */ +EXP_FUNC int STDCALL strcasecmp(const char *s1, const char *s2) +{ + while (tolower(*s1) == tolower(*s2++)) + { + if (*s1++ == '\0') + { + return 0; + } + } + + return *(unsigned char *)s1 - *(unsigned char *)(s2 - 1); +} + + +EXP_FUNC int STDCALL getdomainname(char *buf, int buf_size) +{ + HKEY hKey; + unsigned long datatype; + unsigned long bufferlength = buf_size; + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, + TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), + 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) + return -1; + + RegQueryValueEx(hKey, "Domain", NULL, &datatype, buf, &bufferlength); + RegCloseKey(hKey); + return 0; +} +#endif + +#if 0 +#undef malloc +#undef realloc +#undef calloc + +static const char * out_of_mem_str = "out of memory"; +static const char * file_open_str = "Could not open file \"%s\""; + +/* + * Some functions that call display some error trace and then call abort(). + * This just makes life much easier on embedded systems, since we're + * suffering major trauma... + */ +EXP_FUNC void * STDCALL ax_malloc(size_t s) +{ + void *x; + + if ((x = malloc(s)) == NULL) + exit_now(out_of_mem_str); + + return x; +} + +EXP_FUNC void * STDCALL ax_realloc(void *y, size_t s) +{ + void *x; + + if ((x = realloc(y, s)) == NULL) + exit_now(out_of_mem_str); + + return x; +} + +EXP_FUNC void * STDCALL ax_calloc(size_t n, size_t s) +{ + void *x; + + if ((x = calloc(n, s)) == NULL) + exit_now(out_of_mem_str); + + return x; +} + +EXP_FUNC int STDCALL ax_open(const char *pathname, int flags) +{ + int x; + + if ((x = open(pathname, flags)) < 0) + exit_now(file_open_str, pathname); + + return x; +} + +/** + * This is a call which will deliberately exit an application, but will + * display some information before dying. + */ +void exit_now(const char *format, ...) +{ + va_list argp; + + va_start(argp, format); + vfprintf(stderr, format, argp); + va_end(argp); + abort(); +} + + +/** + * gettimeofday() not in Win32 + */ +EXP_FUNC void STDCALL gettimeofday(struct timeval* t, void* timezone) +{ +#if defined(_WIN32_WCE) + t->tv_sec = time(NULL); + t->tv_usec = 0; /* 1sec precision only */ +#else + /* wujg : pass compile first */ + t->tv_sec = 0; + t->tv_usec = 0; /* 1ms precision */ +#endif +} +#endif + diff --git a/app/ssl/ssl/ssl_p12.c b/app/ssl/ssl/ssl_p12.c new file mode 100644 index 00000000..4b12a68c --- /dev/null +++ b/app/ssl/ssl/ssl_p12.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Process PKCS#8/PKCS#12 keys. + * + * The decoding of a PKCS#12 key is fairly specific - this code was tested on a + * key generated with: + * + * openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem + * -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 + * -name "p12_withoutCA" -out axTLS.withoutCA.p12 -password pass:abcd + * + * or with a certificate chain: + * + * openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem + * -certfile axTLS.ca_x509.pem -keypbe PBE-SHA1-RC4-128 -certpbe + * PBE-SHA1-RC4-128 -name "p12_withCA" -out axTLS.withCA.p12 -password pass:abcd + * + * Note that the PBE has to be specified with PBE-SHA1-RC4-128. The + * private/public keys/certs have to use RSA encryption. Both the integrity + * and privacy passwords are the same. + * + * The PKCS#8 files were generated with something like: + * + * PEM format: + * openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -v1 + * PBE-SHA1-RC4-128 -out axTLS.encrypted_pem.p8 + * + * DER format: + * openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -outform DER + * -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted.p8 + */ + +//#include +//#include +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_ssl.h" + +/* all commented out if not used */ +#ifdef CONFIG_SSL_USE_PKCS12 + +#define BLOCK_SIZE 64 +#define PKCS12_KEY_ID 1 +#define PKCS12_IV_ID 2 +#define PKCS12_MAC_ID 3 + +static char *make_uni_pass(const char *password, int *uni_pass_len); +static int p8_decrypt(const char *uni_pass, int uni_pass_len, + const uint8_t *salt, int iter, + uint8_t *priv_key, int priv_key_len, int id); +static int p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key); +static int get_pbe_params(uint8_t *buf, int *offset, + const uint8_t **salt, int *iterations); + +/* + * Take a raw pkcs8 block and then decrypt it and turn it into a normal key. + */ +int ICACHE_FLASH_ATTR pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password) +{ + uint8_t *buf = ssl_obj->buf; + int len, offset = 0; + int iterations; + int ret = SSL_NOT_OK; + uint8_t *version = NULL; + const uint8_t *salt; + uint8_t *priv_key; + int uni_pass_len; + char *uni_pass = make_uni_pass(password, &uni_pass_len); + + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0) + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("Error: Invalid p8 ASN.1 file\n"); +#endif + goto error; + } + + /* unencrypted key? */ + if (asn1_get_int(buf, &offset, &version) > 0 && *version == 0) + { + ret = p8_add_key(ssl_ctx, buf); + goto error; + } + + if (get_pbe_params(buf, &offset, &salt, &iterations) < 0) + goto error; + + if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0) + goto error; + + priv_key = &buf[offset]; + + p8_decrypt(uni_pass, uni_pass_len, salt, + iterations, priv_key, len, PKCS12_KEY_ID); + ret = p8_add_key(ssl_ctx, priv_key); + +error: + os_free(version); + os_free(uni_pass); + return ret; +} + +/* + * Take the unencrypted pkcs8 and turn it into a private key + */ +static int ICACHE_FLASH_ATTR p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key) +{ + uint8_t *buf = priv_key; + int len, offset = 0; + int ret = SSL_NOT_OK; + + /* Skip the preamble and go straight to the private key. + We only support rsaEncryption (1.2.840.113549.1.1.1) */ + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 || + asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0) + goto error; + + ret = asn1_get_private_key(&buf[offset], len, &ssl_ctx->rsa_ctx); + +error: + return ret; +} + +/* + * Create the unicode password + */ +static char * ICACHE_FLASH_ATTR make_uni_pass(const char *password, int *uni_pass_len) +{ + int pass_len = 0, i; + char *uni_pass; + + if (password == NULL) + { + password = ""; + } + + uni_pass = (char *)os_malloc((os_strlen(password)+1)*2); + + /* modify the password into a unicode version */ + for (i = 0; i < (int)os_strlen(password); i++) + { + uni_pass[pass_len++] = 0; + uni_pass[pass_len++] = password[i]; + } + + uni_pass[pass_len++] = 0; /* null terminate */ + uni_pass[pass_len++] = 0; + *uni_pass_len = pass_len; + return uni_pass; +} + +/* + * Decrypt a pkcs8 block. + */ +static int ICACHE_FLASH_ATTR p8_decrypt(const char *uni_pass, int uni_pass_len, + const uint8_t *salt, int iter, + uint8_t *priv_key, int priv_key_len, int id) +{ + uint8_t p[BLOCK_SIZE*2]; + uint8_t d[BLOCK_SIZE]; + uint8_t Ai[SHA1_SIZE]; + SHA1_CTX sha_ctx; + RC4_CTX rc4_ctx; + int i; + + for (i = 0; i < BLOCK_SIZE; i++) + { + p[i] = salt[i % SALT_SIZE]; + p[BLOCK_SIZE+i] = uni_pass[i % uni_pass_len]; + d[i] = id; + } + + /* get the key - no IV since we are using RC4 */ + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, d, sizeof(d)); + SHA1_Update(&sha_ctx, p, sizeof(p)); + SHA1_Final(Ai, &sha_ctx); + + for (i = 1; i < iter; i++) + { + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, Ai, SHA1_SIZE); + SHA1_Final(Ai, &sha_ctx); + } + + /* do the decryption */ + if (id == PKCS12_KEY_ID) + { + RC4_setup(&rc4_ctx, Ai, 16); + RC4_crypt(&rc4_ctx, priv_key, priv_key, priv_key_len); + } + else /* MAC */ + os_memcpy(priv_key, Ai, SHA1_SIZE); + + return 0; +} + +/* + * Take a raw pkcs12 block and the decrypt it and turn it into a certificate(s) + * and keys. + */ +int ICACHE_FLASH_ATTR pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password) +{ + uint8_t *buf = ssl_obj->buf; + int len, iterations, auth_safes_start, + auth_safes_end, auth_safes_len, key_offset, offset = 0; + int all_certs = 0; + uint8_t *version = NULL, *auth_safes = NULL, *cert, *orig_mac; + uint8_t key[SHA1_SIZE]; + uint8_t mac[SHA1_SIZE]; + const uint8_t *salt; + int uni_pass_len, ret = SSL_OK; + char *uni_pass = make_uni_pass(password, &uni_pass_len); + static const uint8_t pkcs_data[] = /* pkc7 data */ + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01 }; + static const uint8_t pkcs_encrypted[] = /* pkc7 encrypted */ + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06 }; + static const uint8_t pkcs8_key_bag[] = /* 1.2.840.113549.1.12.10.1.2 */ + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02 }; + + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0) + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("Error: Invalid p12 ASN.1 file\n"); +#endif + goto error; + } + + if (asn1_get_int(buf, &offset, &version) < 0 || *version != 3) + { + ret = SSL_ERROR_INVALID_VERSION; + goto error; + } + + /* remove all the boring pcks7 bits */ + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + len != sizeof(pkcs_data) || + os_memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data))) + goto error; + + offset += len; + + if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0) + goto error; + + /* work out the MAC start/end points (done on AuthSafes) */ + auth_safes_start = offset; + auth_safes_end = offset; + if (asn1_skip_obj(buf, &auth_safes_end, ASN1_SEQUENCE) < 0) + goto error; + + auth_safes_len = auth_safes_end - auth_safes_start; + auth_safes = os_malloc(auth_safes_len); + + os_memcpy(auth_safes, &buf[auth_safes_start], auth_safes_len); + + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + (len != sizeof(pkcs_encrypted) || + os_memcmp(&buf[offset], pkcs_encrypted, sizeof(pkcs_encrypted)))) + goto error; + + offset += len; + + if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + len != sizeof(pkcs_data) || + os_memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data))) + goto error; + + offset += len; + + /* work out the salt for the certificate */ + if (get_pbe_params(buf, &offset, &salt, &iterations) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_IMPLICIT_TAG)) < 0) + goto error; + + /* decrypt the certificate */ + cert = &buf[offset]; + if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert, + len, PKCS12_KEY_ID)) < 0) + goto error; + + offset += len; + + /* load the certificate */ + key_offset = 0; + all_certs = asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE); + + /* keep going until all certs are loaded */ + while (key_offset < all_certs) + { + int cert_offset = key_offset; + + if (asn1_skip_obj(cert, &cert_offset, ASN1_SEQUENCE) < 0 || + asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 || + asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 || + asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 || + (len = asn1_next_obj(cert, &key_offset, ASN1_OCTET_STRING)) < 0) + goto error; + + if ((ret = add_cert(ssl_ctx, &cert[key_offset], len)) < 0) + goto error; + + key_offset = cert_offset; + } + + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + len != sizeof(pkcs_data) || + os_memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data))) + goto error; + + offset += len; + + if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + (len != sizeof(pkcs8_key_bag)) || + os_memcmp(&buf[offset], pkcs8_key_bag, sizeof(pkcs8_key_bag))) + goto error; + + offset += len; + + /* work out the salt for the private key */ + if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + get_pbe_params(buf, &offset, &salt, &iterations) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0) + goto error; + + /* decrypt the private key */ + cert = &buf[offset]; + if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert, + len, PKCS12_KEY_ID)) < 0) + goto error; + + offset += len; + + /* load the private key */ + if ((ret = p8_add_key(ssl_ctx, cert)) < 0) + goto error; + + /* miss out on friendly name, local key id etc */ + if (asn1_skip_obj(buf, &offset, ASN1_SET) < 0) + goto error; + + /* work out the MAC */ + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 || + len != SHA1_SIZE) + goto error; + + orig_mac = &buf[offset]; + offset += len; + + /* get the salt */ + if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 || len != 8) + goto error; + + salt = &buf[offset]; + + /* work out what the mac should be */ + if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, + key, SHA1_SIZE, PKCS12_MAC_ID)) < 0) + goto error; + + ssl_hmac_sha1(auth_safes, auth_safes_len, key, SHA1_SIZE, mac); + + if (os_memcmp(mac, orig_mac, SHA1_SIZE)) + { + ret = SSL_ERROR_INVALID_HMAC; + goto error; + } + +error: + os_free(version); + os_free(uni_pass); + os_free(auth_safes); + return ret; +} + +/* + * Retrieve the salt/iteration details from a PBE block. + */ +static int ICACHE_FLASH_ATTR get_pbe_params(uint8_t *buf, int *offset, + const uint8_t **salt, int *iterations) +{ + static const uint8_t pbeSH1RC4[] = /* pbeWithSHAAnd128BitRC4 */ + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x01 }; + + int i, len; + uint8_t *iter = NULL; + int error_code = SSL_ERROR_NOT_SUPPORTED; + + /* Get the PBE type */ + if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, offset, ASN1_OID)) < 0) + goto error; + + /* we expect pbeWithSHAAnd128BitRC4 (1.2.840.113549.1.12.1.1) + which is the only algorithm we support */ + if (len != sizeof(pbeSH1RC4) || + os_memcmp(&buf[*offset], pbeSH1RC4, sizeof(pbeSH1RC4))) + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("Error: pkcs8/pkcs12 must use \"PBE-SHA1-RC4-128\"\n"); +#endif + goto error; + } + + *offset += len; + + if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, offset, ASN1_OCTET_STRING)) < 0 || + len != 8) + goto error; + + *salt = &buf[*offset]; + *offset += len; + + if ((len = asn1_get_int(buf, offset, &iter)) < 0) + goto error; + + *iterations = 0; + for (i = 0; i < len; i++) + { + (*iterations) <<= 8; + (*iterations) += iter[i]; + } + + os_free(iter); + error_code = SSL_OK; /* got here - we are ok */ + +error: + return error_code; +} + +#endif diff --git a/app/ssl/ssl/ssl_tls1.c b/app/ssl/ssl/ssl_tls1.c new file mode 100644 index 00000000..48c16976 --- /dev/null +++ b/app/ssl/ssl/ssl_tls1.c @@ -0,0 +1,2250 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Common ssl/tlsv1 code to both the client and server implementations. + */ + +//#include +//#include +//#include +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_ssl.h" +#include "lwip/tcp.h" +#include "ssl/app/espconn_ssl.h" + +extern struct pbuf* psslpbuf; +/* The session expiry time */ +#define SSL_EXPIRY_TIME (CONFIG_SSL_EXPIRY_TIME*3600) + +static const uint8_t g_hello_request[] = { HS_HELLO_REQUEST, 0, 0, 0 }; +static const uint8_t g_chg_cipher_spec_pkt[] = { 1 }; +static const char * server_finished = "server finished"; +static const char * client_finished = "client finished"; + +static int do_handshake(SSL *ssl, uint8_t *buf, int read_len); +static int set_key_block(SSL *ssl, int is_write); +static int verify_digest(SSL *ssl, int mode, const uint8_t *buf, int read_len); +static void *crypt_new(SSL *ssl, uint8_t *key, uint8_t *iv, int is_decrypt); +static err_t send_raw_packet(SSL *ssl, uint8_t protocol); + +/** + * The server will pick the cipher based on the order that the order that the + * ciphers are listed. This order is defined at compile time. + */ +#ifdef CONFIG_SSL_SKELETON_MODE +const uint8_t ssl_prot_prefs[NUM_PROTOCOLS] = +{ SSL_RC4_128_SHA }; +#else +static void session_free(SSL_SESSION *ssl_sessions[], int sess_index); + +const uint8_t ssl_prot_prefs[NUM_PROTOCOLS] = +#ifdef CONFIG_SSL_PROT_LOW /* low security, fast speed */ +{ SSL_RC4_128_SHA, SSL_AES128_SHA, SSL_AES256_SHA, SSL_RC4_128_MD5 }; +#elif CONFIG_SSL_PROT_MEDIUM /* medium security, medium speed */ +{ SSL_AES128_SHA, SSL_AES256_SHA, SSL_RC4_128_SHA, SSL_RC4_128_MD5 }; +#else /* CONFIG_SSL_PROT_HIGH */ /* high security, low speed */ +{ SSL_AES256_SHA, SSL_AES128_SHA, SSL_RC4_128_SHA, SSL_RC4_128_MD5 }; +#endif +#endif /* CONFIG_SSL_SKELETON_MODE */ + +/** + * The cipher map containing all the essentials for each cipher. + */ +#ifdef CONFIG_SSL_SKELETON_MODE +static const cipher_info_t cipher_info[NUM_PROTOCOLS] = +{ + { /* RC4-SHA */ + SSL_RC4_128_SHA, /* RC4-SHA */ + 16, /* key size */ + 0, /* iv size */ + 2*(SHA1_SIZE+16), /* key block size */ + 0, /* no padding */ + SHA1_SIZE, /* digest size */ + ssl_hmac_sha1, /* hmac algorithm */ + (crypt_func)RC4_crypt, /* encrypt */ + (crypt_func)RC4_crypt /* decrypt */ + }, +}; +#else +static const cipher_info_t cipher_info[NUM_PROTOCOLS] = +{ + { /* AES128-SHA */ + SSL_AES128_SHA, /* AES128-SHA */ + 16, /* key size */ + 16, /* iv size */ + 2*(SHA1_SIZE+16+16), /* key block size */ + 16, /* block padding size */ + SHA1_SIZE, /* digest size */ + ssl_hmac_sha1, /* hmac algorithm */ + (crypt_func)AES_cbc_encrypt, /* encrypt */ + (crypt_func)AES_cbc_decrypt /* decrypt */ + }, + { /* AES256-SHA */ + SSL_AES256_SHA, /* AES256-SHA */ + 32, /* key size */ + 16, /* iv size */ + 2*(SHA1_SIZE+32+16), /* key block size */ + 16, /* block padding size */ + SHA1_SIZE, /* digest size */ + ssl_hmac_sha1, /* hmac algorithm */ + (crypt_func)AES_cbc_encrypt, /* encrypt */ + (crypt_func)AES_cbc_decrypt /* decrypt */ + }, + { /* RC4-SHA */ + SSL_RC4_128_SHA, /* RC4-SHA */ + 16, /* key size */ + 0, /* iv size */ + 2*(SHA1_SIZE+16), /* key block size */ + 0, /* no padding */ + SHA1_SIZE, /* digest size */ + ssl_hmac_sha1, /* hmac algorithm */ + (crypt_func)RC4_crypt, /* encrypt */ + (crypt_func)RC4_crypt /* decrypt */ + }, + /* + * This protocol is from SSLv2 days and is unlikely to be used - but was + * useful for testing different possible digest algorithms. + */ + { /* RC4-MD5 */ + SSL_RC4_128_MD5, /* RC4-MD5 */ + 16, /* key size */ + 0, /* iv size */ + 2*(MD5_SIZE+16), /* key block size */ + 0, /* no padding */ + MD5_SIZE, /* digest size */ + ssl_hmac_md5, /* hmac algorithm */ + (crypt_func)RC4_crypt, /* encrypt */ + (crypt_func)RC4_crypt /* decrypt */ + }, +}; +#endif + +static void prf(const uint8_t *sec, int sec_len, uint8_t *seed, int seed_len, + uint8_t *out, int olen); +static const cipher_info_t *get_cipher_info(uint8_t cipher); +static void increment_read_sequence(SSL *ssl); +static void increment_write_sequence(SSL *ssl); +static void add_hmac_digest(SSL *ssl, int snd, uint8_t *hmac_header, + const uint8_t *buf, int buf_len, uint8_t *hmac_buf); + +/* win32 VC6.0 doesn't have variadic macros */ +#if defined(WIN32) && !defined(CONFIG_SSL_FULL_MODE) +void DISPLAY_BYTES(SSL *ssl, const char *format, + const uint8_t *data, int size, ...) {} +#endif + +/** + * Establish a new client/server context. + */ +EXP_FUNC SSL_CTX *STDCALL ICACHE_FLASH_ATTR ssl_ctx_new(uint32_t options, int num_sessions) +{ + SSL_CTX *ssl_ctx = (SSL_CTX *)os_zalloc(sizeof (SSL_CTX)); + ssl_ctx->options = options; + RNG_initialize(); + + if (load_key_certs(ssl_ctx) < 0) + { + os_free(ssl_ctx); /* can't load our key/certificate pair, so die */ + return NULL; + } + +#ifndef CONFIG_SSL_SKELETON_MODE + ssl_ctx->num_sessions = num_sessions; +#endif + + SSL_CTX_MUTEX_INIT(ssl_ctx->mutex); + +#ifndef CONFIG_SSL_SKELETON_MODE + if (num_sessions) + { + ssl_ctx->ssl_sessions = (SSL_SESSION **) + os_zalloc(num_sessions*sizeof(SSL_SESSION *)); + } +#endif + + return ssl_ctx; +} + +/* + * Remove a client/server context. + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR ssl_ctx_free(SSL_CTX *ssl_ctx) +{ + SSL *ssl; + int i; + + if (ssl_ctx == NULL) + return; + + ssl = ssl_ctx->head; + + /* clear out all the ssl entries */ + while (ssl) + { + SSL *next = ssl->next; + ssl_free(ssl); + ssl = next; + } + +#ifndef CONFIG_SSL_SKELETON_MODE + /* clear out all the sessions */ + for (i = 0; i < ssl_ctx->num_sessions; i++) + session_free(ssl_ctx->ssl_sessions, i); + + os_free(ssl_ctx->ssl_sessions); +#endif + + i = 0; + while (i < CONFIG_SSL_MAX_CERTS && ssl_ctx->certs[i].buf) + { + os_free(ssl_ctx->certs[i].buf); + ssl_ctx->certs[i++].buf = NULL; + } + +#ifdef CONFIG_SSL_CERT_VERIFICATION + remove_ca_certs(ssl_ctx->ca_cert_ctx); +#endif + ssl_ctx->chain_length = 0; + SSL_CTX_MUTEX_DESTROY(ssl_ctx->mutex); +// ssl_printf("%s %p\n", __func__,ssl_ctx->rsa_ctx); + RSA_free(ssl_ctx->rsa_ctx); + RNG_terminate(); +// ssl_printf("%s %p\n", __func__,ssl_ctx); + os_free(ssl_ctx); +} + +/* + * Free any used resources used by this connection. + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR ssl_free(SSL *ssl) +{ + SSL_CTX *ssl_ctx; + + if (ssl == NULL) /* just ignore null pointers */ + return; + + /* only notify if we weren't notified first */ + /* spec says we must notify when we are dying */ + if (!IS_SET_SSL_FLAG(SSL_SENT_CLOSE_NOTIFY)) + send_alert(ssl, SSL_ALERT_CLOSE_NOTIFY); +// ssl_printf("%s %d\n", __func__, __LINE__); + + ssl_ctx = ssl->ssl_ctx; + + SSL_CTX_LOCK(ssl_ctx->mutex); + + /* adjust the server SSL list */ + if (ssl->prev) + ssl->prev->next = ssl->next; + else + ssl_ctx->head = ssl->next; + + if (ssl->next) + ssl->next->prev = ssl->prev; + else + ssl_ctx->tail = ssl->prev; + + SSL_CTX_UNLOCK(ssl_ctx->mutex); + + /* may already be free - but be sure */ + os_free(ssl->encrypt_ctx); + os_free(ssl->decrypt_ctx); + disposable_free(ssl); +#ifdef CONFIG_SSL_CERT_VERIFICATION + x509_free(ssl->x509_ctx); +#endif + + os_free(ssl); +} + +/* + * Read the SSL connection and send any alerts for various errors. + */ +EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_read(SSL *ssl, uint8_t **in_data) +{ + int ret = basic_read(ssl, in_data); + + /* check for return code so we can send an alert */ + if (ret < SSL_OK && ret != SSL_CLOSE_NOTIFY) + { + if (ret != SSL_ERROR_CONN_LOST) + { + send_alert(ssl, ret); +#ifndef CONFIG_SSL_SKELETON_MODE + /* something nasty happened, so get rid of this session */ + kill_ssl_session(ssl->ssl_ctx->ssl_sessions, ssl); +#endif + } + } + + return ret; +} + +/* + * Write application data to the client + */ +EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_write(SSL *ssl, const uint8_t *out_data, int out_len) +{ + int n = out_len, nw, i, tot = 0; + + /* maximum size of a TLS packet is around 16kB, so fragment */ + do + { + nw = n; + + if (nw > RT_MAX_PLAIN_LENGTH) /* fragment if necessary */ + nw = RT_MAX_PLAIN_LENGTH; + + if ((i = send_packet(ssl, PT_APP_PROTOCOL_DATA, + &out_data[tot], nw)) <= 0) + { + out_len = i; /* an error */ + break; + } + + tot += i; + n -= i; + } while (n > 0); + + return out_len; +} + +/** + * Add a certificate to the certificate chain. + */ +int ICACHE_FLASH_ATTR add_cert(SSL_CTX *ssl_ctx, const uint8_t *buf, int len) +{ + int ret = SSL_ERROR_NO_CERT_DEFINED, i = 0; + SSL_CERT *ssl_cert; + X509_CTX *cert = NULL; + int offset; + + while (ssl_ctx->certs[i].buf && i < CONFIG_SSL_MAX_CERTS) + i++; + + if (i == CONFIG_SSL_MAX_CERTS) /* too many certs */ + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("Error: maximum number of certs added (%d) - change of " + "compile-time configuration required\n", + CONFIG_SSL_MAX_CERTS); +#endif + goto error; + } + + if ((ret = x509_new(buf, &offset, &cert))) + goto error; + +#if defined (CONFIG_SSL_FULL_MODE) + if (ssl_ctx->options & SSL_DISPLAY_CERTS) + x509_print(cert, NULL); +#endif + + ssl_cert = &ssl_ctx->certs[i]; + ssl_cert->size = len; + ssl_cert->buf = (uint8_t *)os_malloc(len); + os_memcpy(ssl_cert->buf, buf, len); + ssl_ctx->chain_length++; + len -= offset; + ret = SSL_OK; /* ok so far */ + + /* recurse? */ + if (len > 0) + { + ret = add_cert(ssl_ctx, &buf[offset], len); + } + +error: + x509_free(cert); /* don't need anymore */ + return ret; +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Add a certificate authority. + */ +int ICACHE_FLASH_ATTR add_cert_auth(SSL_CTX *ssl_ctx, const uint8_t *buf, int len) +{ + int ret = SSL_OK; /* ignore errors for now */ + int i = 0; + CA_CERT_CTX *ca_cert_ctx; + + if (ssl_ctx->ca_cert_ctx == NULL) + ssl_ctx->ca_cert_ctx = (CA_CERT_CTX *)os_zalloc(sizeof(CA_CERT_CTX)); + + ca_cert_ctx = ssl_ctx->ca_cert_ctx; + + while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) + i++; + + while (len > 0) + { + int offset; + if (i >= CONFIG_X509_MAX_CA_CERTS) + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("Error: maximum number of CA certs added (%d) - change of " + "compile-time configuration required\n", + CONFIG_X509_MAX_CA_CERTS); +#endif + break; + } + + + /* ignore the return code */ + if (x509_new(buf, &offset, &ca_cert_ctx->cert[i]) == X509_OK) + { +#if defined (CONFIG_SSL_FULL_MODE) + if (ssl_ctx->options & SSL_DISPLAY_CERTS) + x509_print(ca_cert_ctx->cert[i], NULL); +#endif + } + + i++; + len -= offset; + } + + return ret; +} + +/* + * Retrieve an X.509 distinguished name component + */ +EXP_FUNC const char * STDCALL ICACHE_FLASH_ATTR ssl_get_cert_dn(const SSL *ssl, int component) +{ + if (ssl->x509_ctx == NULL) + return NULL; + + switch (component) + { + case SSL_X509_CERT_COMMON_NAME: + return ssl->x509_ctx->cert_dn[X509_COMMON_NAME]; + + case SSL_X509_CERT_ORGANIZATION: + return ssl->x509_ctx->cert_dn[X509_ORGANIZATION]; + + case SSL_X509_CERT_ORGANIZATIONAL_NAME: + return ssl->x509_ctx->cert_dn[X509_ORGANIZATIONAL_UNIT]; + + case SSL_X509_CA_CERT_COMMON_NAME: + return ssl->x509_ctx->ca_cert_dn[X509_COMMON_NAME]; + + case SSL_X509_CA_CERT_ORGANIZATION: + return ssl->x509_ctx->ca_cert_dn[X509_ORGANIZATION]; + + case SSL_X509_CA_CERT_ORGANIZATIONAL_NAME: + return ssl->x509_ctx->ca_cert_dn[X509_ORGANIZATIONAL_UNIT]; + + default: + return NULL; + } +} + +/* + * Retrieve a "Subject Alternative Name" from a v3 certificate + */ +EXP_FUNC const char * STDCALL ICACHE_FLASH_ATTR ssl_get_cert_subject_alt_dnsname(const SSL *ssl, + int dnsindex) +{ + int i; + + if (ssl->x509_ctx == NULL || ssl->x509_ctx->subject_alt_dnsnames == NULL) + return NULL; + + for (i = 0; i < dnsindex; ++i) + { + if (ssl->x509_ctx->subject_alt_dnsnames[i] == NULL) + return NULL; + } + + return ssl->x509_ctx->subject_alt_dnsnames[dnsindex]; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ +#if 0 +/* + * Find an ssl object based on the client's file descriptor. + */ +EXP_FUNC SSL * STDCALL ICACHE_FLASH_ATTR ssl_find(SSL_CTX *ssl_ctx, int client_fd) +{ + SSL *ssl; + + SSL_CTX_LOCK(ssl_ctx->mutex); + ssl = ssl_ctx->head; + + /* search through all the ssl entries */ + while (ssl) + { + if (ssl->client_fd == client_fd) + { + SSL_CTX_UNLOCK(ssl_ctx->mutex); + return ssl; + } + + ssl = ssl->next; + } + + SSL_CTX_UNLOCK(ssl_ctx->mutex); + return NULL; +} +#endif +/* + * Force the client to perform its handshake again. + */ +EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_renegotiate(SSL *ssl) +{ + int ret = SSL_OK; + + disposable_new(ssl); +#ifdef CONFIG_SSL_ENABLE_CLIENT + if (IS_SET_SSL_FLAG(SSL_IS_CLIENT)) + { + ret = do_client_connect(ssl); + } + else +#endif + { + send_packet(ssl, PT_HANDSHAKE_PROTOCOL, + g_hello_request, sizeof(g_hello_request)); + SET_SSL_FLAG(SSL_NEED_RECORD); + } + + return ret; +} + +/** + * @brief Get what we need for key info. + * @param cipher [in] The cipher information we are after + * @param key_size [out] The key size for the cipher + * @param iv_size [out] The iv size for the cipher + * @return The amount of key information we need. + */ +static const cipher_info_t *ICACHE_FLASH_ATTR get_cipher_info(uint8_t cipher) +{ + int i; + + for (i = 0; i < NUM_PROTOCOLS; i++) + { + if (cipher_info[i].cipher == cipher) + { + return &cipher_info[i]; + } + } + + return NULL; /* error */ +} + +#if 0 +/* + * Get a new ssl context for a new connection. + */ +SSL *ICACHE_FLASH_ATTR ssl_new(SSL_CTX *ssl_ctx, int client_fd) +{ + SSL *ssl = (SSL *)os_zalloc(sizeof(SSL)); + ssl->ssl_ctx = ssl_ctx; + ssl->need_bytes = SSL_RECORD_SIZE; /* need a record */ + ssl->client_fd = client_fd; + ssl->flag = SSL_NEED_RECORD; + ssl->bm_data = ssl->bm_all_data+BM_RECORD_OFFSET; /* space at the start */ + ssl->hs_status = SSL_NOT_OK; /* not connected */ +#ifdef CONFIG_ENABLE_VERIFICATION + ssl->ca_cert_ctx = ssl_ctx->ca_cert_ctx; +#endif + disposable_new(ssl); + + /* a bit hacky but saves a few bytes of memory */ + ssl->flag |= ssl_ctx->options; + SSL_CTX_LOCK(ssl_ctx->mutex); + + if (ssl_ctx->head == NULL) + { + ssl_ctx->head = ssl; + ssl_ctx->tail = ssl; + } + else + { + ssl->prev = ssl_ctx->tail; + ssl_ctx->tail->next = ssl; + ssl_ctx->tail = ssl; + } + + SSL_CTX_UNLOCK(ssl_ctx->mutex); + return ssl; +} +#endif + +/* + * Get a new ssl context for a new connection.(raw api)add by ives 12.12.2013 + */ +SSL *ICACHE_FLASH_ATTR ssl_new_context(SSL_CTX *ssl_ctx, struct tcp_pcb *SslClient_pcb) +{ + SSL *ssl = (SSL *)os_zalloc(sizeof(SSL)); + ssl->ssl_ctx = ssl_ctx; + ssl->need_bytes = SSL_RECORD_SIZE; /* need a record */ + //ssl->client_fd = client_fd;annotation by ives 12.12.2013 + ssl->SslClient_pcb = SslClient_pcb; + ssl->ssl_pbuf = NULL; + ssl->flag = SSL_NEED_RECORD; + ssl->bm_data = ssl->bm_all_data + BM_RECORD_OFFSET; /* space at the start */ + ssl->hs_status = SSL_NOT_OK; /* not connected */ +#ifdef CONFIG_ENABLE_VERIFICATION + ssl->ca_cert_ctx = ssl_ctx->ca_cert_ctx; +#endif + disposable_new(ssl); + /* a bit hacky but saves a few bytes of memory */ + ssl->flag |= ssl_ctx->options; + SSL_CTX_LOCK(ssl_ctx->mutex); + if (ssl_ctx->head == NULL) { + ssl_ctx->head = ssl; + ssl_ctx->tail = ssl; + } else { + ssl->prev = ssl_ctx->tail; + ssl_ctx->tail->next = ssl; + ssl_ctx->tail = ssl; + } + SSL_CTX_UNLOCK(ssl_ctx->mutex); + return ssl; +} +/* + * Add a private key to a context. + */ +int ICACHE_FLASH_ATTR add_private_key(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj) +{ + int ret = SSL_OK; + + /* get the private key details */ + if (asn1_get_private_key(ssl_obj->buf, ssl_obj->len, &ssl_ctx->rsa_ctx)) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + +error: + return ret; +} + +/** + * Increment the read sequence number (as a 64 bit endian indepenent #) + */ +static void ICACHE_FLASH_ATTR increment_read_sequence(SSL *ssl) +{ + int i; + + for (i = 7; i >= 0; i--) + { + if (++ssl->read_sequence[i]) + break; + } +} + +/** + * Increment the read sequence number (as a 64 bit endian indepenent #) + */ +static void ICACHE_FLASH_ATTR increment_write_sequence(SSL *ssl) +{ + int i; + + for (i = 7; i >= 0; i--) + { + if (++ssl->write_sequence[i]) + break; + } +} + +/** + * Work out the HMAC digest in a packet. + */ +static void ICACHE_FLASH_ATTR add_hmac_digest(SSL *ssl, int mode, uint8_t *hmac_header, + const uint8_t *buf, int buf_len, uint8_t *hmac_buf) +{ + int hmac_len = buf_len + 8 + SSL_RECORD_SIZE; + uint8_t *t_buf = (uint8_t *)os_malloc(hmac_len+10); + + os_memcpy(t_buf, (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_WRITE) ? + ssl->write_sequence : ssl->read_sequence, 8); + os_memcpy(&t_buf[8], hmac_header, SSL_RECORD_SIZE); + os_memcpy(&t_buf[8+SSL_RECORD_SIZE], buf, buf_len); + + ssl->cipher_info->hmac(t_buf, hmac_len, + (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_READ) ? + ssl->server_mac : ssl->client_mac, + ssl->cipher_info->digest_size, hmac_buf); + + /* add by wujg */ + os_free(t_buf); + +#if 0 + print_blob("record", hmac_header, SSL_RECORD_SIZE); + print_blob("buf", buf, buf_len); + if (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_WRITE) + { + print_blob("write seq", ssl->write_sequence, 8); + } + else + { + print_blob("read seq", ssl->read_sequence, 8); + } + + if (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_READ) + { + print_blob("server mac", + ssl->server_mac, ssl->cipher_info->digest_size); + } + else + { + print_blob("client mac", + ssl->client_mac, ssl->cipher_info->digest_size); + } + print_blob("hmac", hmac_buf, SHA1_SIZE); +#endif +} + +/** + * Verify that the digest of a packet is correct. + */ +static int ICACHE_FLASH_ATTR verify_digest(SSL *ssl, int mode, const uint8_t *buf, int read_len) +{ + uint8_t hmac_buf[SHA1_SIZE]; + int hmac_offset; + + if (ssl->cipher_info->padding_size) + { + int last_blk_size = buf[read_len-1], i; + hmac_offset = read_len-last_blk_size-ssl->cipher_info->digest_size-1; + + /* guard against a timing attack - make sure we do the digest */ + if (hmac_offset < 0) + { + hmac_offset = 0; + } + else + { + /* already looked at last byte */ + for (i = 1; i < last_blk_size; i++) + { + if (buf[read_len-i] != last_blk_size) + { + hmac_offset = 0; + break; + } + } + } + } + else /* stream cipher */ + { + hmac_offset = read_len - ssl->cipher_info->digest_size; + + if (hmac_offset < 0) + { + hmac_offset = 0; + } + } + + /* sanity check the offset */ + ssl->hmac_header[3] = hmac_offset >> 8; /* insert size */ + ssl->hmac_header[4] = hmac_offset & 0xff; + add_hmac_digest(ssl, mode, ssl->hmac_header, buf, hmac_offset, hmac_buf); + + if (memcmp(hmac_buf, &buf[hmac_offset], ssl->cipher_info->digest_size)) + { + return SSL_ERROR_INVALID_HMAC; + } + + return hmac_offset; +} + +/** + * Add a packet to the end of our sent and received packets, so that we may use + * it to calculate the hash at the end. + */ +void ICACHE_FLASH_ATTR add_packet(SSL *ssl, const uint8_t *pkt, int len) +{ + MD5_Update(&ssl->dc->md5_ctx, pkt, len); + SHA1_Update(&ssl->dc->sha1_ctx, pkt, len); +} + +/** + * Work out the MD5 PRF. + */ +static void ICACHE_FLASH_ATTR p_hash_md5(const uint8_t *sec, int sec_len, + uint8_t *seed, int seed_len, uint8_t *out, int olen) +{ + uint8_t a1[128]; + + /* A(1) */ + ssl_hmac_md5(seed, seed_len, sec, sec_len, a1); + os_memcpy(&a1[MD5_SIZE], seed, seed_len); + ssl_hmac_md5(a1, MD5_SIZE+seed_len, sec, sec_len, out); + + while (olen > MD5_SIZE) + { + uint8_t a2[MD5_SIZE]; + out += MD5_SIZE; + olen -= MD5_SIZE; + + /* A(N) */ + ssl_hmac_md5(a1, MD5_SIZE, sec, sec_len, a2); + os_memcpy(a1, a2, MD5_SIZE); + + /* work out the actual hash */ + ssl_hmac_md5(a1, MD5_SIZE+seed_len, sec, sec_len, out); + } +} + +/** + * Work out the SHA1 PRF. + */ +static void ICACHE_FLASH_ATTR p_hash_sha1(const uint8_t *sec, int sec_len, + uint8_t *seed, int seed_len, uint8_t *out, int olen) +{ + uint8_t a1[128]; + + /* A(1) */ + ssl_hmac_sha1(seed, seed_len, sec, sec_len, a1); + os_memcpy(&a1[SHA1_SIZE], seed, seed_len); + ssl_hmac_sha1(a1, SHA1_SIZE+seed_len, sec, sec_len, out); + + while (olen > SHA1_SIZE) + { + uint8_t a2[SHA1_SIZE]; + out += SHA1_SIZE; + olen -= SHA1_SIZE; + + /* A(N) */ + ssl_hmac_sha1(a1, SHA1_SIZE, sec, sec_len, a2); + os_memcpy(a1, a2, SHA1_SIZE); + + /* work out the actual hash */ + ssl_hmac_sha1(a1, SHA1_SIZE+seed_len, sec, sec_len, out); + } +} + +/** + * Work out the PRF. + */ +static void ICACHE_FLASH_ATTR prf(const uint8_t *sec, int sec_len, uint8_t *seed, int seed_len, + uint8_t *out, int olen) +{ + int len, i; + const uint8_t *S1, *S2; + uint8_t xbuf[256]; /* needs to be > the amount of key data */ + uint8_t ybuf[256]; /* needs to be > the amount of key data */ + + len = sec_len/2; + S1 = sec; + S2 = &sec[len]; + len += (sec_len & 1); /* add for odd, make longer */ + + p_hash_md5(S1, len, seed, seed_len, xbuf, olen); + p_hash_sha1(S2, len, seed, seed_len, ybuf, olen); + + for (i = 0; i < olen; i++) + out[i] = xbuf[i] ^ ybuf[i]; +} + +/** + * Generate a master secret based on the client/server random data and the + * premaster secret. + */ +void ICACHE_FLASH_ATTR generate_master_secret(SSL *ssl, const uint8_t *premaster_secret) +{ + uint8_t buf[128]; /* needs to be > 13+32+32 in size */ + os_strcpy((char *)buf, "master secret"); + os_memcpy(&buf[13], ssl->dc->client_random, SSL_RANDOM_SIZE); + os_memcpy(&buf[45], ssl->dc->server_random, SSL_RANDOM_SIZE); + prf(premaster_secret, SSL_SECRET_SIZE, buf, 77, ssl->dc->master_secret, + SSL_SECRET_SIZE); +} + +/** + * Generate a 'random' blob of data used for the generation of keys. + */ +static void ICACHE_FLASH_ATTR generate_key_block(uint8_t *client_random, uint8_t *server_random, + uint8_t *master_secret, uint8_t *key_block, int key_block_size) +{ + uint8_t buf[128]; + os_strcpy((char *)buf, "key expansion"); + os_memcpy(&buf[13], server_random, SSL_RANDOM_SIZE); + os_memcpy(&buf[45], client_random, SSL_RANDOM_SIZE); + prf(master_secret, SSL_SECRET_SIZE, buf, 77, key_block, key_block_size); +} + +/** + * Calculate the digest used in the finished message. This function also + * doubles up as a certificate verify function. + */ +void ICACHE_FLASH_ATTR finished_digest(SSL *ssl, const char *label, uint8_t *digest) +{ + uint8_t mac_buf[128]; + uint8_t *q = mac_buf; + MD5_CTX md5_ctx = ssl->dc->md5_ctx; + SHA1_CTX sha1_ctx = ssl->dc->sha1_ctx; + + if (label) + { + os_strcpy((char *)q, label); + q += os_strlen(label); + } + + MD5_Final(q, &md5_ctx); + q += MD5_SIZE; + + SHA1_Final(q, &sha1_ctx); + q += SHA1_SIZE; + + if (label) + { + prf(ssl->dc->master_secret, SSL_SECRET_SIZE, mac_buf, (int)(q-mac_buf), + digest, SSL_FINISHED_HASH_SIZE); + } + else /* for use in a certificate verify */ + { + os_memcpy(digest, mac_buf, MD5_SIZE + SHA1_SIZE); + } + +#if 0 + printf("label: %s\n", label); + print_blob("master secret", ssl->dc->master_secret, 48); + print_blob("mac_buf", mac_buf, q-mac_buf); + print_blob("finished digest", digest, SSL_FINISHED_HASH_SIZE); +#endif +} + +/** + * Retrieve (and initialise) the context of a cipher. + */ +static void *ICACHE_FLASH_ATTR crypt_new(SSL *ssl, uint8_t *key, uint8_t *iv, int is_decrypt) +{ + switch (ssl->cipher) + { +#ifndef CONFIG_SSL_SKELETON_MODE + case SSL_AES128_SHA: + { + AES_CTX *aes_ctx = (AES_CTX *)os_malloc(sizeof(AES_CTX)); + AES_set_key(aes_ctx, key, iv, AES_MODE_128); + + if (is_decrypt) + { + AES_convert_key(aes_ctx); + } + + return (void *)aes_ctx; + } + + case SSL_AES256_SHA: + { + AES_CTX *aes_ctx = (AES_CTX *)os_malloc(sizeof(AES_CTX)); + AES_set_key(aes_ctx, key, iv, AES_MODE_256); + + if (is_decrypt) + { + AES_convert_key(aes_ctx); + } + + return (void *)aes_ctx; + } + + case SSL_RC4_128_MD5: +#endif + case SSL_RC4_128_SHA: + { + RC4_CTX *rc4_ctx = (RC4_CTX *)os_malloc(sizeof(RC4_CTX)); + RC4_setup(rc4_ctx, key, 16); + return (void *)rc4_ctx; + } + } + + return NULL; /* its all gone wrong */ +} + +/** + * Send a packet over the socket. + */ +static err_t ICACHE_FLASH_ATTR send_raw_packet(SSL *ssl, uint8_t protocol) +{ + uint8_t *rec_buf = ssl->bm_all_data; + int pkt_size = SSL_RECORD_SIZE + ssl->bm_index; + int Length = 0; + //int ret = SSL_OK; + err_t Err = ERR_OK; + rec_buf[0] = protocol; + rec_buf[1] = 0x03; /* version = 3.1 or higher */ + rec_buf[2] = ssl->version & 0x0f; + rec_buf[3] = ssl->bm_index >> 8; + rec_buf[4] = ssl->bm_index & 0xff; + //DISPLAY_BYTES(ssl, "sending %d bytes", ssl->bm_all_data, + // pkt_size, pkt_size); + + ssl_printf("send_raw_packet pkt_size %d\n", pkt_size); + if(tcp_sndbuf(ssl->SslClient_pcb) < pkt_size) { + Length = tcp_sndbuf(ssl->SslClient_pcb); + } else { + Length = pkt_size; + } + if(Length > 2 * ssl->SslClient_pcb->mss) { + Length = 2 * ssl->SslClient_pcb->mss; + } + do { + Err = tcp_write(ssl->SslClient_pcb, &ssl->bm_all_data[0], Length, 0); + if (Err == ERR_MEM) { + Length /= 2; + } + } while(Err == ERR_MEM && Length > 1); + ssl_printf("send_raw_packet Length %d\n", Length); + if (Err == ERR_OK) { + Err = tcp_output(ssl->SslClient_pcb); + } + SET_SSL_FLAG(SSL_NEED_RECORD); /* reset for next time */ + //ssl->bm_index = 0; + if (protocol != PT_APP_PROTOCOL_DATA) { + /* always return SSL_OK during handshake */ + ssl->bm_index = 0; + Err = SSL_OK; + } + return Err; +} + +/** + * Send an encrypted packet with padding bytes if necessary. + */ +int ICACHE_FLASH_ATTR send_packet(SSL *ssl, uint8_t protocol, const uint8_t *in, int length) +{ + int ret, msg_length = 0; + + /* if our state is bad, don't bother */ + if (ssl->hs_status == SSL_ERROR_DEAD) + return SSL_ERROR_CONN_LOST; + + if (in) /* has the buffer already been initialised? */ + { + os_memcpy(ssl->bm_data, in, length); + } + + msg_length += length; + + if (IS_SET_SSL_FLAG(SSL_TX_ENCRYPTED)) + { + int mode = IS_SET_SSL_FLAG(SSL_IS_CLIENT) ? + SSL_CLIENT_WRITE : SSL_SERVER_WRITE; + uint8_t hmac_header[SSL_RECORD_SIZE] = + { + protocol, + 0x03, /* version = 3.1 or higher */ + ssl->version & 0x0f, + msg_length >> 8, + msg_length & 0xff + }; + + if (protocol == PT_HANDSHAKE_PROTOCOL) + { + //DISPLAY_STATE(ssl, 1, ssl->bm_data[0], 0); + + if (ssl->bm_data[0] != HS_HELLO_REQUEST) + { + add_packet(ssl, ssl->bm_data, msg_length); + } + } + + /* add the packet digest */ + add_hmac_digest(ssl, mode, hmac_header, ssl->bm_data, msg_length, + &ssl->bm_data[msg_length]); + msg_length += ssl->cipher_info->digest_size; + + /* add padding? */ + if (ssl->cipher_info->padding_size) + { + int last_blk_size = msg_length%ssl->cipher_info->padding_size; + int pad_bytes = ssl->cipher_info->padding_size - last_blk_size; + + /* ensure we always have at least 1 padding byte */ + if (pad_bytes == 0) + pad_bytes += ssl->cipher_info->padding_size; + + os_memset(&ssl->bm_data[msg_length], pad_bytes-1, pad_bytes); + msg_length += pad_bytes; + } + + //DISPLAY_BYTES(ssl, "unencrypted write", ssl->bm_data, msg_length); + increment_write_sequence(ssl); + + /* add the explicit IV for TLS1.1 */ + if (ssl->version >= SSL_PROTOCOL_VERSION1_1 && + ssl->cipher_info->iv_size) + { + uint8_t iv_size = ssl->cipher_info->iv_size; + uint8_t *t_buf = (uint8_t *)os_malloc(msg_length + iv_size); + os_memcpy(t_buf + iv_size, ssl->bm_data, msg_length); + get_random(iv_size, t_buf); + msg_length += iv_size; + os_memcpy(ssl->bm_data, t_buf, msg_length); + os_free(t_buf); /* add by wujg */ + } + + /* now encrypt the packet */ + ssl->cipher_info->encrypt(ssl->encrypt_ctx, ssl->bm_data, + ssl->bm_data, msg_length); + } + else if (protocol == PT_HANDSHAKE_PROTOCOL) + { + //DISPLAY_STATE(ssl, 1, ssl->bm_data[0], 0); + + if (ssl->bm_data[0] != HS_HELLO_REQUEST) + { + add_packet(ssl, ssl->bm_data, length); + } + } + + ssl->bm_index = msg_length; + if ((ret = send_raw_packet(ssl, protocol)) <= 0) + return ret; + + return length; /* just return what we wanted to send */ +} + +/** + * Work out the cipher keys we are going to use for this session based on the + * master secret. + */ +static int ICACHE_FLASH_ATTR set_key_block(SSL *ssl, int is_write) +{ + const cipher_info_t *ciph_info = get_cipher_info(ssl->cipher); + uint8_t *q; + uint8_t client_key[32], server_key[32]; /* big enough for AES256 */ + uint8_t client_iv[16], server_iv[16]; /* big enough for AES128/256 */ + int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + + if (ciph_info == NULL) + return -1; + + /* only do once in a handshake */ + if (ssl->dc->key_block == NULL) + { + ssl->dc->key_block = (uint8_t *)os_malloc(ciph_info->key_block_size); + +#if 0 + print_blob("client", ssl->dc->client_random, 32); + print_blob("server", ssl->dc->server_random, 32); + print_blob("master", ssl->dc->master_secret, SSL_SECRET_SIZE); +#endif + generate_key_block(ssl->dc->client_random, ssl->dc->server_random, + ssl->dc->master_secret, ssl->dc->key_block, + ciph_info->key_block_size); +#if 0 + print_blob("keyblock", ssl->dc->key_block, ciph_info->key_block_size); +#endif + } + + q = ssl->dc->key_block; + + if ((is_client && is_write) || (!is_client && !is_write)) + { + os_memcpy(ssl->client_mac, q, ciph_info->digest_size); + } + + q += ciph_info->digest_size; + + if ((!is_client && is_write) || (is_client && !is_write)) + { + os_memcpy(ssl->server_mac, q, ciph_info->digest_size); + } + + q += ciph_info->digest_size; + os_memcpy(client_key, q, ciph_info->key_size); + q += ciph_info->key_size; + os_memcpy(server_key, q, ciph_info->key_size); + q += ciph_info->key_size; + +#ifndef CONFIG_SSL_SKELETON_MODE + if (ciph_info->iv_size) /* RC4 has no IV, AES does */ + { + os_memcpy(client_iv, q, ciph_info->iv_size); + q += ciph_info->iv_size; + os_memcpy(server_iv, q, ciph_info->iv_size); + q += ciph_info->iv_size; + } +#endif + + os_free(is_write ? ssl->encrypt_ctx : ssl->decrypt_ctx); + + /* now initialise the ciphers */ + if (is_client) + { + finished_digest(ssl, server_finished, ssl->dc->final_finish_mac); + + if (is_write) + ssl->encrypt_ctx = crypt_new(ssl, client_key, client_iv, 0); + else + ssl->decrypt_ctx = crypt_new(ssl, server_key, server_iv, 1); + } + else + { + finished_digest(ssl, client_finished, ssl->dc->final_finish_mac); + + if (is_write) + ssl->encrypt_ctx = crypt_new(ssl, server_key, server_iv, 0); + else + ssl->decrypt_ctx = crypt_new(ssl, client_key, client_iv, 1); + } + + ssl->cipher_info = ciph_info; + return 0; +} + +/** + * Read the SSL connection. + */ +int ICACHE_FLASH_ATTR basic_read(SSL *ssl, uint8_t **in_data) +{ +int ret = SSL_OK; + int j,i = 0; + int read_len, is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + uint8_t *buf = ssl->bm_data; + uint8_t *read_buf = NULL; + uint8_t *pread_buf = NULL; + u16_t recvlength = 0; + read_buf =(uint8_t*)os_zalloc(ssl->ssl_pbuf->len + 1); + pread_buf = read_buf; + if (pread_buf != NULL){ + recvlength = pbuf_copy_partial(ssl->ssl_pbuf, read_buf,ssl->ssl_pbuf->len,0); + } + if (recvlength != 0){ + do{ +// ssl_printf("basic_read ssl->bm_read_index %d\n", ssl->bm_read_index); +// ssl_printf("basic_read ssl->need_bytes %d\n", ssl->need_bytes); +// ssl_printf("basic_read ssl->got_bytes %d\n", ssl->got_bytes); + read_len = ssl->need_bytes - ssl->got_bytes; + if (read_len >= recvlength){ + read_len = recvlength; + } + os_memcpy(&buf[ssl->bm_read_index],read_buf, read_len); +// ssl_printf("basic_read read_len %d\n", read_len); +// for (i = ssl->bm_read_index; i < (ssl->bm_read_index + read_len); i ++){ +// ssl_printf("%2x ",buf[i]); +// if ((i + 1) % 16 == 0) +// ssl_printf("\n"); +// } +// ssl_printf("\n"); + + read_buf += read_len; + recvlength -= read_len; +// ssl_printf("basic_read %d %d\n", __LINE__, recvlength); + /* connection has gone, so die */ + if (read_len <= 0) + { + ret = SSL_ERROR_CONN_LOST; + ssl->hs_status = SSL_ERROR_DEAD; /* make sure it stays dead */ + goto error; + } + + //DISPLAY_BYTES(ssl, "received %d bytes", + // &ssl->bm_data[ssl->bm_read_index], read_len, read_len); + + ssl->got_bytes += read_len; + ssl->bm_read_index += read_len; + + /* haven't quite got what we want, so try again later */ + if (ssl->got_bytes < ssl->need_bytes){ +// ssl_printf("basic_read %d %p\n", __LINE__, pread_buf); + os_free(pread_buf); + pread_buf = NULL; + return SSL_OK; + } + read_len = ssl->got_bytes; + ssl->got_bytes = 0; + + if (IS_SET_SSL_FLAG(SSL_NEED_RECORD)) + { + /* check for sslv2 "client hello" */ + if (buf[0] & 0x80 && buf[2] == 1) + { +#ifdef CONFIG_SSL_ENABLE_V23_HANDSHAKE + uint8_t version = (buf[3] << 4) + buf[4]; + DISPLAY_BYTES(ssl, "ssl2 record", buf, 5); + + /* should be v3.1 (TLSv1) or better */ + ssl->version = ssl->client_version = version; + + if (version > SSL_PROTOCOL_VERSION_MAX) + { + /* use client's version */ + ssl->version = SSL_PROTOCOL_VERSION_MAX; + } + else if (version < SSL_PROTOCOL_MIN_VERSION) + { + ret = SSL_ERROR_INVALID_VERSION; + ssl_display_error(ret); + return ret; + } + + add_packet(ssl, &buf[2], 3); + ret = process_sslv23_client_hello(ssl); +#else + ssl_printf("Error: no SSLv23 handshaking allowed\n"); //TTY_FLUSH(); + ret = SSL_ERROR_NOT_SUPPORTED; +#endif + goto error; /* not an error - just get out of here */ + } + + ssl->need_bytes = (buf[3] << 8) + buf[4]; + + /* do we violate the spec with the message size? */ + if (ssl->need_bytes > RT_MAX_PLAIN_LENGTH+RT_EXTRA-BM_RECORD_OFFSET) + { + ret = SSL_ERROR_INVALID_PROT_MSG; + recvlength = 0; + os_printf("we violate the spec with the message size\n"); + goto error; + } + + CLR_SSL_FLAG(SSL_NEED_RECORD); + os_memcpy(ssl->hmac_header, buf, 3); /* store for hmac */ + ssl->record_type = buf[0]; + goto error; /* no error, we're done */ + } + + /* for next time - just do it now in case of an error */ + SET_SSL_FLAG(SSL_NEED_RECORD); + ssl->need_bytes = SSL_RECORD_SIZE; + + /* decrypt if we need to */ + if (IS_SET_SSL_FLAG(SSL_RX_ENCRYPTED)) + { + ssl->cipher_info->decrypt(ssl->decrypt_ctx, buf, buf, read_len); + + if (ssl->version >= SSL_PROTOCOL_VERSION1_1 && + ssl->cipher_info->iv_size) + { + buf += ssl->cipher_info->iv_size; + read_len -= ssl->cipher_info->iv_size; + } + + read_len = verify_digest(ssl, + is_client ? SSL_CLIENT_READ : SSL_SERVER_READ, buf, read_len); + + /* does the hmac work? */ + if (read_len < 0) + { + ret = read_len; + goto error; + } + + //DISPLAY_BYTES(ssl, "decrypted", buf, read_len); + increment_read_sequence(ssl); + } + + /* The main part of the SSL packet */ + //ssl_printf("basic_read %d %x %p\n", __LINE__, ssl->record_type, in_data); + switch (ssl->record_type) + { + case PT_HANDSHAKE_PROTOCOL: + if (ssl->dc != NULL) + { + ssl->dc->bm_proc_index = 0; + ret = do_handshake(ssl, buf, read_len); + } + else /* no client renegotiation allowed */ + { + ret = SSL_ERROR_NO_CLIENT_RENOG; + goto error; + } + break; + + case PT_CHANGE_CIPHER_SPEC: + if (ssl->next_state != HS_FINISHED) + { + ret = SSL_ERROR_INVALID_HANDSHAKE; + goto error; + } + + /* all encrypted from now on */ + SET_SSL_FLAG(SSL_RX_ENCRYPTED); + if (set_key_block(ssl, 0) < 0) + { + ret = SSL_ERROR_INVALID_HANDSHAKE; + goto error; + } + + os_memset(ssl->read_sequence, 0, 8); + break; + + case PT_APP_PROTOCOL_DATA: + if (in_data) + { + *in_data = buf; /* point to the work buffer */ + (*in_data)[read_len] = 0; /* null terminate just in case */ + } + + ret = read_len; + recvlength = 0; + break; + + case PT_ALERT_PROTOCOL: + /* return the alert # with alert bit set */ + if(buf[0] == SSL_ALERT_TYPE_WARNING && + buf[1] == SSL_ALERT_CLOSE_NOTIFY) + { + ret = SSL_CLOSE_NOTIFY; + //send_alert(ssl, SSL_ALERT_CLOSE_NOTIFY); + SET_SSL_FLAG(SSL_SENT_CLOSE_NOTIFY); + } + else + { + ret = -buf[1]; + //DISPLAY_ALERT(ssl, buf[1]); + } + + break; + + default: + ret = SSL_ERROR_INVALID_PROT_MSG; + break; + } + +error: + ssl->bm_read_index = 0; /* reset to go again */ + + if (ret < SSL_OK && in_data)/* if all wrong, then clear this buffer ptr */ + *in_data = NULL; + }while(recvlength != 0); + }else{ + ssl_printf("%s %d %d\n", __func__, __LINE__,recvlength); + } + os_free(pread_buf); + pread_buf = NULL; + return ret; +} + +/** + * Do some basic checking of data and then perform the appropriate handshaking. + */ +static int ICACHE_FLASH_ATTR do_handshake(SSL *ssl, uint8_t *buf, int read_len) +{ + int hs_len = (buf[2]<<8) + buf[3]; + uint8_t handshake_type = buf[0]; + int ret = SSL_OK; + int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + + /* some integrity checking on the handshake */ + PARANOIA_CHECK(read_len-SSL_HS_HDR_SIZE, hs_len); + + if (handshake_type != ssl->next_state) + { + /* handle a special case on the client */ + if (!is_client || handshake_type != HS_CERT_REQ || + ssl->next_state != HS_SERVER_HELLO_DONE) + { + ret = SSL_ERROR_INVALID_HANDSHAKE; + goto error; + } + } + + hs_len += SSL_HS_HDR_SIZE; /* adjust for when adding packets */ + ssl->bm_index = hs_len; /* store the size and check later */ + //DISPLAY_STATE(ssl, 0, handshake_type, 0); + + if (handshake_type != HS_CERT_VERIFY && handshake_type != HS_HELLO_REQUEST) + add_packet(ssl, buf, hs_len); + +#if defined(CONFIG_SSL_ENABLE_CLIENT) + ret = is_client ? + do_clnt_handshake(ssl, handshake_type, buf, hs_len) : + do_svr_handshake(ssl, handshake_type, buf, hs_len); +#else + ret = do_svr_handshake(ssl, handshake_type, buf, hs_len); +#endif + + /* just use recursion to get the rest */ + if (hs_len < read_len && ret == SSL_OK) + ret = do_handshake(ssl, &buf[hs_len], read_len-hs_len); + +error: + return ret; +} + +/** + * Sends the change cipher spec message. We have just read a finished message + * from the client. + */ +int ICACHE_FLASH_ATTR send_change_cipher_spec(SSL *ssl) +{ + int ret = send_packet(ssl, PT_CHANGE_CIPHER_SPEC, + g_chg_cipher_spec_pkt, sizeof(g_chg_cipher_spec_pkt)); + SET_SSL_FLAG(SSL_TX_ENCRYPTED); + + if (ret >= 0 && set_key_block(ssl, 1) < 0) + ret = SSL_ERROR_INVALID_HANDSHAKE; + + os_memset(ssl->write_sequence, 0, 8); + return ret; +} + +/** + * Send a "finished" message + */ +int ICACHE_FLASH_ATTR send_finished(SSL *ssl) +{ + uint8_t buf[SSL_FINISHED_HASH_SIZE+4] = { + HS_FINISHED, 0, 0, SSL_FINISHED_HASH_SIZE }; + + /* now add the finished digest mac (12 bytes) */ + finished_digest(ssl, + IS_SET_SSL_FLAG(SSL_IS_CLIENT) ? + client_finished : server_finished, &buf[4]); + +#ifndef CONFIG_SSL_SKELETON_MODE + /* store in the session cache */ + if (!IS_SET_SSL_FLAG(SSL_SESSION_RESUME) && ssl->ssl_ctx->num_sessions) + { + os_memcpy(ssl->session->master_secret, + ssl->dc->master_secret, SSL_SECRET_SIZE); + } +#endif + + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, + buf, SSL_FINISHED_HASH_SIZE+4); +} + +/** + * Send an alert message. + * Return 1 if the alert was an "error". + */ +int ICACHE_FLASH_ATTR send_alert(SSL *ssl, int error_code) +{ + int alert_num = 0; + int is_warning = 0; + uint8_t buf[2]; + + /* Don't bother we're already dead */ + if (ssl->hs_status == SSL_ERROR_DEAD) + { + return SSL_ERROR_CONN_LOST; + } + +#ifdef CONFIG_SSL_FULL_MODE + //if (IS_SET_SSL_FLAG(SSL_DISPLAY_STATES)) + //ssl_display_error(error_code); +#endif + + switch (error_code) + { + case SSL_ALERT_CLOSE_NOTIFY: + is_warning = 1; + alert_num = SSL_ALERT_CLOSE_NOTIFY; + break; + + case SSL_ERROR_CONN_LOST: /* don't send alert just yet */ + is_warning = 1; + break; + + case SSL_ERROR_INVALID_HANDSHAKE: + case SSL_ERROR_INVALID_PROT_MSG: + alert_num = SSL_ALERT_HANDSHAKE_FAILURE; + break; + + case SSL_ERROR_INVALID_HMAC: + case SSL_ERROR_FINISHED_INVALID: + alert_num = SSL_ALERT_BAD_RECORD_MAC; + break; + + case SSL_ERROR_INVALID_VERSION: + alert_num = SSL_ALERT_INVALID_VERSION; + break; + + case SSL_ERROR_INVALID_SESSION: + case SSL_ERROR_NO_CIPHER: + case SSL_ERROR_INVALID_KEY: + alert_num = SSL_ALERT_ILLEGAL_PARAMETER; + break; + + case SSL_ERROR_BAD_CERTIFICATE: + alert_num = SSL_ALERT_BAD_CERTIFICATE; + break; + + case SSL_ERROR_NO_CLIENT_RENOG: + alert_num = SSL_ALERT_NO_RENEGOTIATION; + break; + + default: + /* a catch-all for any badly verified certificates */ + alert_num = (error_code <= SSL_X509_OFFSET) ? + SSL_ALERT_BAD_CERTIFICATE : SSL_ALERT_UNEXPECTED_MESSAGE; + break; + } + + buf[0] = is_warning ? 1 : 2; + buf[1] = alert_num; + send_packet(ssl, PT_ALERT_PROTOCOL, buf, sizeof(buf)); + //DISPLAY_ALERT(ssl, alert_num); + return is_warning ? 0 : 1; +} + +/** + * Process a client finished message. + */ +int ICACHE_FLASH_ATTR process_finished(SSL *ssl, uint8_t *buf, int hs_len) +{ + int ret = SSL_OK; + int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + int resume = IS_SET_SSL_FLAG(SSL_SESSION_RESUME); + + PARANOIA_CHECK(ssl->bm_index, SSL_FINISHED_HASH_SIZE+4); + + /* check that we all work before we continue */ + if (os_memcmp(ssl->dc->final_finish_mac, &buf[4], SSL_FINISHED_HASH_SIZE)) + return SSL_ERROR_FINISHED_INVALID; + + if ((!is_client && !resume) || (is_client && resume)) + { + if ((ret = send_change_cipher_spec(ssl)) == SSL_OK) + ret = send_finished(ssl); + } + + /* if we ever renegotiate */ + ssl->next_state = is_client ? HS_HELLO_REQUEST : HS_CLIENT_HELLO; + ssl->hs_status = ret; /* set the final handshake status */ + +error: + return ret; +} + +/** + * Send a certificate. + */ +int ICACHE_FLASH_ATTR send_certificate(SSL *ssl) +{ + int i = 0; + uint8_t *buf = ssl->bm_data; + int offset = 7; + int chain_length; + + buf[0] = HS_CERTIFICATE; + buf[1] = 0; + buf[4] = 0; + + while (i < ssl->ssl_ctx->chain_length) + { + SSL_CERT *cert = &ssl->ssl_ctx->certs[i]; + buf[offset++] = 0; + buf[offset++] = cert->size >> 8; /* cert 1 length */ + buf[offset++] = cert->size & 0xff; + os_memcpy(&buf[offset], cert->buf, cert->size); + offset += cert->size; + i++; + } + + chain_length = offset - 7; + buf[5] = chain_length >> 8; /* cert chain length */ + buf[6] = chain_length & 0xff; + chain_length += 3; + buf[2] = chain_length >> 8; /* handshake length */ + buf[3] = chain_length & 0xff; + ssl->bm_index = offset; + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset); +} + +/** + * Create a blob of memory that we'll get rid of once the handshake is + * complete. + */ +void ICACHE_FLASH_ATTR disposable_new(SSL *ssl) +{ + if (ssl->dc == NULL) + { + ssl->dc = (DISPOSABLE_CTX *)os_zalloc(sizeof(DISPOSABLE_CTX)); + MD5_Init(&ssl->dc->md5_ctx); + SHA1_Init(&ssl->dc->sha1_ctx); + } +} + +/** + * Remove the temporary blob of memory. + */ +void ICACHE_FLASH_ATTR disposable_free(SSL *ssl) +{ + if (ssl->dc) + { + os_free(ssl->dc->key_block); + os_memset(ssl->dc, 0, sizeof(DISPOSABLE_CTX)); + os_free(ssl->dc); + ssl->dc = NULL; + } + +} + +#ifndef CONFIG_SSL_SKELETON_MODE /* no session resumption in this mode */ +/** + * Find if an existing session has the same session id. If so, use the + * master secret from this session for session resumption. + */ +SSL_SESSION *ICACHE_FLASH_ATTR ssl_session_update(int max_sessions, SSL_SESSION *ssl_sessions[], + SSL *ssl, const uint8_t *session_id) +{ + time_t tm = 0; //time(NULL); wujg + time_t oldest_sess_time = tm; + SSL_SESSION *oldest_sess = NULL; + int i; + + /* no sessions? Then bail */ + if (max_sessions == 0) + return NULL; + + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + if (session_id) + { + for (i = 0; i < max_sessions; i++) + { + if (ssl_sessions[i]) + { + /* kill off any expired sessions (including those in + the future) */ + if ((tm > ssl_sessions[i]->conn_time + SSL_EXPIRY_TIME) || + (tm < ssl_sessions[i]->conn_time)) + { + session_free(ssl_sessions, i); + continue; + } + + /* if the session id matches, it must still be less than + the expiry time */ + if (os_memcmp(ssl_sessions[i]->session_id, session_id, + SSL_SESSION_ID_SIZE) == 0) + { + ssl->session_index = i; + os_memcpy(ssl->dc->master_secret, + ssl_sessions[i]->master_secret, SSL_SECRET_SIZE); + SET_SSL_FLAG(SSL_SESSION_RESUME); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + return ssl_sessions[i]; /* a session was found */ + } + } + } + } + + /* If we've got here, no matching session was found - so create one */ + for (i = 0; i < max_sessions; i++) + { + if (ssl_sessions[i] == NULL) + { + /* perfect, this will do */ + ssl_sessions[i] = (SSL_SESSION *)os_zalloc(sizeof(SSL_SESSION)); + ssl_sessions[i]->conn_time = tm; + ssl->session_index = i; + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + return ssl_sessions[i]; /* return the session object */ + } + else if (ssl_sessions[i]->conn_time <= oldest_sess_time) + { + /* find the oldest session */ + oldest_sess_time = ssl_sessions[i]->conn_time; + oldest_sess = ssl_sessions[i]; + ssl->session_index = i; + } + } + + /* ok, we've used up all of our sessions. So blow the oldest session away */ + oldest_sess->conn_time = tm; + os_memset(oldest_sess->session_id, 0, sizeof(SSL_SESSION_ID_SIZE)); + os_memset(oldest_sess->master_secret, 0, sizeof(SSL_SECRET_SIZE)); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + return oldest_sess; +} + +/** + * Free an existing session. + */ +static void ICACHE_FLASH_ATTR session_free(SSL_SESSION *ssl_sessions[], int sess_index) +{ + if (ssl_sessions[sess_index]) + { + os_free(ssl_sessions[sess_index]); + ssl_sessions[sess_index] = NULL; + } +} + +/** + * This ssl object doesn't want this session anymore. + */ +void ICACHE_FLASH_ATTR kill_ssl_session(SSL_SESSION **ssl_sessions, SSL *ssl) +{ + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + + if (ssl->ssl_ctx->num_sessions) + { + session_free(ssl_sessions, ssl->session_index); + ssl->session = NULL; + } + + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); +} +#endif /* CONFIG_SSL_SKELETON_MODE */ + +/* + * Get the session id for a handshake. This will be a 32 byte sequence. + */ +EXP_FUNC const uint8_t * STDCALL ICACHE_FLASH_ATTR ssl_get_session_id(const SSL *ssl) +{ + return ssl->session_id; +} + +/* + * Get the session id size for a handshake. + */ +EXP_FUNC uint8_t STDCALL ICACHE_FLASH_ATTR ssl_get_session_id_size(const SSL *ssl) +{ + return ssl->sess_id_size; +} + +/* + * Return the cipher id (in the SSL form). + */ +EXP_FUNC uint8_t STDCALL ICACHE_FLASH_ATTR ssl_get_cipher_id(const SSL *ssl) +{ + return ssl->cipher; +} + +/* + * Return the status of the handshake. + */ +EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_handshake_status(const SSL *ssl) +{ + return ssl->hs_status; +} + +/* + * Retrieve various parameters about the SSL engine. + */ +EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_get_config(int offset) +{ + switch (offset) + { + /* return the appropriate build mode */ + case SSL_BUILD_MODE: +#if defined(CONFIG_SSL_FULL_MODE) + return SSL_BUILD_FULL_MODE; +#elif defined(CONFIG_SSL_ENABLE_CLIENT) + return SSL_BUILD_ENABLE_CLIENT; +#elif defined(CONFIG_ENABLE_VERIFICATION) + return SSL_BUILD_ENABLE_VERIFICATION; +#elif defined(CONFIG_SSL_SERVER_ONLY ) + return SSL_BUILD_SERVER_ONLY; +#else + return SSL_BUILD_SKELETON_MODE; +#endif + + case SSL_MAX_CERT_CFG_OFFSET: + return CONFIG_SSL_MAX_CERTS; + +#ifdef CONFIG_SSL_CERT_VERIFICATION + case SSL_MAX_CA_CERT_CFG_OFFSET: + return CONFIG_X509_MAX_CA_CERTS; +#endif +#ifdef CONFIG_SSL_HAS_PEM + case SSL_HAS_PEM: + return 1; +#endif + default: + return 0; + } +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Authenticate a received certificate. + */ +EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_verify_cert(const SSL *ssl) +{ + int ret; + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + ret = x509_verify(ssl->ssl_ctx->ca_cert_ctx, ssl->x509_ctx); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + if (ret) /* modify into an SSL error type */ + { + ret = SSL_X509_ERROR(ret); + } + + return ret; +} + +/** + * Process a certificate message. + */ +int ICACHE_FLASH_ATTR process_certificate(SSL *ssl, X509_CTX **x509_ctx) +{ + int ret = SSL_OK; + uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index]; + int pkt_size = ssl->bm_index; + int cert_size, offset = 5; + int total_cert_size = (buf[offset]<<8) + buf[offset+1]; + int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + X509_CTX **chain = x509_ctx; + offset += 2; + + PARANOIA_CHECK(total_cert_size, offset); + + while (offset < total_cert_size) + { + offset++; /* skip empty char */ + cert_size = (buf[offset]<<8) + buf[offset+1]; + offset += 2; + + if (x509_new(&buf[offset], NULL, chain)) + { + ret = SSL_ERROR_BAD_CERTIFICATE; + goto error; + } + + chain = &((*chain)->next); + offset += cert_size; + } + + PARANOIA_CHECK(pkt_size, offset); + + /* if we are client we can do the verify now or later */ + if (is_client && !IS_SET_SSL_FLAG(SSL_SERVER_VERIFY_LATER)) + { + ret = ssl_verify_cert(ssl); + } + + ssl->next_state = is_client ? HS_SERVER_HELLO_DONE : HS_CLIENT_KEY_XCHG; + ssl->dc->bm_proc_index += offset; +error: + return ret; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ + +/** + * Debugging routine to display SSL handshaking stuff. + */ +#ifdef CONFIG_SSL_FULL_MODE +/** + * Debugging routine to display SSL states. + */ +#if 0 +void ICACHE_FLASH_ATTR DISPLAY_STATE(SSL *ssl, int is_send, uint8_t state, int not_ok) +{ + const char *str; + + if (!IS_SET_SSL_FLAG(SSL_DISPLAY_STATES)) + return; + + ssl_printf(not_ok ? "Error - invalid State:\t" : "State:\t"); + ssl_printf(is_send ? "sending " : "receiving "); + + switch (state) + { + case HS_HELLO_REQUEST: + str = "Hello Request (0)"; + break; + + case HS_CLIENT_HELLO: + str = "Client Hello (1)"; + break; + + case HS_SERVER_HELLO: + str = "Server Hello (2)"; + break; + + case HS_CERTIFICATE: + str = "Certificate (11)"; + break; + + case HS_SERVER_KEY_XCHG: + str = "Certificate Request (12)"; + break; + + case HS_CERT_REQ: + str = "Certificate Request (13)"; + break; + + case HS_SERVER_HELLO_DONE: + str = "Server Hello Done (14)"; + break; + + case HS_CERT_VERIFY: + str = "Certificate Verify (15)"; + break; + + case HS_CLIENT_KEY_XCHG: + str = "Client Key Exchange (16)"; + break; + + case HS_FINISHED: + str = "Finished (16)"; + break; + + default: + str = "Error (Unknown)"; + + break; + } + + ssl_printf("%s\n", str); + //TTY_FLUSH(); +} + +/** + * Debugging routine to display RSA objects + */ +void ICACHE_FLASH_ATTR DISPLAY_RSA(SSL *ssl, const RSA_CTX *rsa_ctx) +{ + if (!IS_SET_SSL_FLAG(SSL_DISPLAY_RSA)) + return; + + RSA_print(rsa_ctx); + //TTY_FLUSH(); +} + +/** + * Debugging routine to display SSL handshaking bytes. + */ +void ICACHE_FLASH_ATTR DISPLAY_BYTES(SSL *ssl, const char *format, + const uint8_t *data, int size, ...) +{ +// wujg : pass compile first +// va_list(ap); + +// if (!IS_SET_SSL_FLAG(SSL_DISPLAY_BYTES)) +// return; + +// va_start(ap, size); +// print_blob(format, data, size, va_arg(ap, char *)); +// va_end(ap); +// TTY_FLUSH(); +} + +/** + * Debugging routine to display SSL handshaking errors. + */ +EXP_FUNC void STDCALL ICACHE_FLASH_ATTR ssl_display_error(int error_code) +{ + if (error_code == SSL_OK) + return; + + ssl_printf("Error: "); + + /* X509 error? */ + if (error_code < SSL_X509_OFFSET) + { + ssl_printf("%s\n", x509_display_error(error_code - SSL_X509_OFFSET)); + return; + } + + /* SSL alert error code */ + if (error_code > SSL_ERROR_CONN_LOST) + { + ssl_printf("SSL error %d\n", -error_code); + return; + } + + switch (error_code) + { + case SSL_ERROR_DEAD: + ssl_printf("connection dead"); + break; + + case SSL_ERROR_INVALID_HANDSHAKE: + ssl_printf("invalid handshake"); + break; + + case SSL_ERROR_INVALID_PROT_MSG: + ssl_printf("invalid protocol message"); + break; + + case SSL_ERROR_INVALID_HMAC: + ssl_printf("invalid mac"); + break; + + case SSL_ERROR_INVALID_VERSION: + ssl_printf("invalid version"); + break; + + case SSL_ERROR_INVALID_SESSION: + ssl_printf("invalid session"); + break; + + case SSL_ERROR_NO_CIPHER: + ssl_printf("no cipher"); + break; + + case SSL_ERROR_CONN_LOST: + ssl_printf("connection lost"); + break; + + case SSL_ERROR_BAD_CERTIFICATE: + ssl_printf("bad certificate"); + break; + + case SSL_ERROR_INVALID_KEY: + ssl_printf("invalid key"); + break; + + case SSL_ERROR_FINISHED_INVALID: + ssl_printf("finished invalid"); + break; + + case SSL_ERROR_NO_CERT_DEFINED: + ssl_printf("no certificate defined"); + break; + + case SSL_ERROR_NO_CLIENT_RENOG: + ssl_printf("client renegotiation not supported"); + break; + + case SSL_ERROR_NOT_SUPPORTED: + ssl_printf("Option not supported"); + break; + + default: + ssl_printf("undefined as yet - %d", error_code); + break; + } + + ssl_printf("\n"); + //TTY_FLUSH(); +} + +/** + * Debugging routine to display alerts. + */ +void ICACHE_FLASH_ATTR DISPLAY_ALERT(SSL *ssl, int alert) +{ + if (!IS_SET_SSL_FLAG(SSL_DISPLAY_STATES)) + return; + + ssl_printf("Alert: "); + + switch (alert) + { + case SSL_ALERT_CLOSE_NOTIFY: + ssl_printf("close notify"); + break; + + case SSL_ALERT_INVALID_VERSION: + ssl_printf("invalid version"); + break; + + case SSL_ALERT_BAD_CERTIFICATE: + ssl_printf("bad certificate"); + break; + + case SSL_ALERT_UNEXPECTED_MESSAGE: + ssl_printf("unexpected message"); + break; + + case SSL_ALERT_BAD_RECORD_MAC: + ssl_printf("bad record mac"); + break; + + case SSL_ALERT_HANDSHAKE_FAILURE: + ssl_printf("handshake failure"); + break; + + case SSL_ALERT_ILLEGAL_PARAMETER: + ssl_printf("illegal parameter"); + break; + + case SSL_ALERT_DECODE_ERROR: + ssl_printf("decode error"); + break; + + case SSL_ALERT_DECRYPT_ERROR: + ssl_printf("decrypt error"); + break; + + case SSL_ALERT_NO_RENEGOTIATION: + ssl_printf("no renegotiation"); + break; + + default: + ssl_printf("alert - (unknown %d)", alert); + break; + } + + ssl_printf("\n"); + //TTY_FLUSH(); +} +#endif +#endif /* CONFIG_SSL_FULL_MODE */ + +/** + * Return the version of this library. + */ +EXP_FUNC const char * STDCALL ICACHE_FLASH_ATTR ssl_version() +{ + static const char * axtls_version = AXTLS_VERSION; + return axtls_version; +} + +/** + * Enable the various language bindings to work regardless of the + * configuration - they just return an error statement and a bad return code. + */ +#if !defined(CONFIG_SSL_FULL_MODE) +EXP_FUNC void STDCALL ssl_display_error(int error_code) {} +#endif + +#ifdef CONFIG_BINDINGS +#if !defined(CONFIG_SSL_ENABLE_CLIENT) +EXP_FUNC SSL * STDCALL ICACHE_FLASH_ATTR ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const + uint8_t *session_id, uint8_t sess_id_size) +{ + ssl_printf(unsupported_str); + return NULL; +} +#endif + +#if !defined(CONFIG_SSL_CERT_VERIFICATION) +EXP_FUNC int STDCALL ICACHE_FLASH_ATTR ssl_verify_cert(const SSL *ssl) +{ + ssl_printf(unsupported_str); + return -1; +} + + +EXP_FUNC const char * STDCALL ICACHE_FLASH_ATTR ssl_get_cert_dn(const SSL *ssl, int component) +{ + ssl_printf(unsupported_str); + return NULL; +} + +EXP_FUNC const char * STDCALL ICACHE_FLASH_ATTR ssl_get_cert_subject_alt_dnsname(const SSL *ssl, int index) +{ + ssl_printf(unsupported_str); + return NULL; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ + +#endif /* CONFIG_BINDINGS */ + diff --git a/app/ssl/ssl/ssl_tls1_clnt.c b/app/ssl/ssl/ssl_tls1_clnt.c new file mode 100644 index 00000000..e248743c --- /dev/null +++ b/app/ssl/ssl/ssl_tls1_clnt.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//#include +//#include +//#include +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_ssl.h" +#include "lwip/tcp.h" +#include "ssl/app/espconn_ssl.h" + +#ifdef CONFIG_SSL_ENABLE_CLIENT /* all commented out if no client */ + +static int send_client_hello(SSL *ssl); +static int process_server_hello(SSL *ssl); +static int process_server_hello_done(SSL *ssl); +static int send_client_key_xchg(SSL *ssl); +static int process_cert_req(SSL *ssl); +static int send_cert_verify(SSL *ssl); + +#if 0 +/* + * Establish a new SSL connection to an SSL server. + */ +EXP_FUNC SSL * STDCALL ICACHE_FLASH_ATTR ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const + uint8_t *session_id, uint8_t sess_id_size) +{ + SSL *ssl = ssl_new(ssl_ctx, client_fd); + ssl->version = SSL_PROTOCOL_VERSION_MAX; /* try top version first */ + + if (session_id && ssl_ctx->num_sessions) + { + if (sess_id_size > SSL_SESSION_ID_SIZE) /* validity check */ + { + ssl_free(ssl); + return NULL; + } + + os_memcpy(ssl->session_id, session_id, sess_id_size); + ssl->sess_id_size = sess_id_size; + SET_SSL_FLAG(SSL_SESSION_RESUME); /* just flag for later */ + } + + SET_SSL_FLAG(SSL_IS_CLIENT); + do_client_connect(ssl); + return ssl; +} +#endif + +/* + * Establish a new SSL connection to an SSL server.(raw api)add by ives 12.12.2013 + */ +EXP_FUNC SSL *STDCALL ICACHE_FLASH_ATTR SSLClient_new(SSL_CTX *ssl_ctx, struct tcp_pcb *SslClient_pcb, const + uint8_t *session_id, uint8_t sess_id_size) +{ + SSL *ssl = ssl_new_context(ssl_ctx, SslClient_pcb); + ssl->version = SSL_PROTOCOL_VERSION_MAX; + if (session_id && ssl_ctx->num_sessions) { + if (sess_id_size > SSL_SESSION_ID_SIZE) { + ssl_free(ssl); + return NULL; + } + os_memcpy(ssl->session_id, session_id, sess_id_size); + ssl->sess_id_size = sess_id_size; + SET_SSL_FLAG(SSL_SESSION_RESUME); + } + SET_SSL_FLAG(SSL_IS_CLIENT); + do_client_connect(ssl); + return ssl; +} +/* + * Process the handshake record. + */ +int ICACHE_FLASH_ATTR do_clnt_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len) +{ + int ret; + + /* To get here the state must be valid */ +// ssl_printf("do_clnt_handshake: %d %d\n",__LINE__, handshake_type); + switch (handshake_type) + { + case HS_SERVER_HELLO: + ret = process_server_hello(ssl); + break; + + case HS_CERTIFICATE: + ret = process_certificate(ssl, &ssl->x509_ctx); + break; + + case HS_SERVER_HELLO_DONE: + if ((ret = process_server_hello_done(ssl)) == SSL_OK) + { + if (IS_SET_SSL_FLAG(SSL_HAS_CERT_REQ)) + { + if ((ret = send_certificate(ssl)) == SSL_OK && + (ret = send_client_key_xchg(ssl)) == SSL_OK) + { + send_cert_verify(ssl); + } + } + else + { + ret = send_client_key_xchg(ssl); + } + + if (ret == SSL_OK && + (ret = send_change_cipher_spec(ssl)) == SSL_OK) + { + ret = send_finished(ssl); + } + } + break; + + case HS_CERT_REQ: + ret = process_cert_req(ssl); + break; + + case HS_FINISHED: + ret = process_finished(ssl, buf, hs_len); + disposable_free(ssl); /* free up some memory */ + /* note: client renegotiation is not allowed after this */ + break; + + case HS_HELLO_REQUEST: + disposable_new(ssl); + ret = do_client_connect(ssl); + break; + + default: + ret = SSL_ERROR_INVALID_HANDSHAKE; + break; + } + + return ret; +} + +/* + * Do the handshaking from the beginning. + */ +int ICACHE_FLASH_ATTR do_client_connect(SSL *ssl) +{ + int ret = SSL_OK; + + send_client_hello(ssl); /* send the client hello */ + ssl->bm_read_index = 0; + ssl->next_state = HS_SERVER_HELLO; + ssl->hs_status = SSL_NOT_OK; /* not connected */ +#if 0 + /* sit in a loop until it all looks good */ + if (!IS_SET_SSL_FLAG(SSL_CONNECT_IN_PARTS)) + { + while (ssl->hs_status != SSL_OK) + { + ret = ssl_read(ssl, NULL); + ssl_printf("%s %d %d\n", __func__, __LINE__,ret); + if (ret < SSL_OK) + break; + } + + ssl->hs_status = ret; /* connected? */ + } +#endif + return ret; +} + +/* + * Send the initial client hello. + */ +static int ICACHE_FLASH_ATTR send_client_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + time_t tm = 0; //time(NULL); wujg : pass compile first + uint8_t *tm_ptr = &buf[6]; /* time will go here */ + int i, offset; + + buf[0] = HS_CLIENT_HELLO; + buf[1] = 0; + buf[2] = 0; + /* byte 3 is calculated later */ + buf[4] = 0x03; + buf[5] = ssl->version & 0x0f; + + /* client random value - spec says that 1st 4 bytes are big endian time */ + *tm_ptr++ = (uint8_t)(((long)tm & 0xff000000) >> 24); + *tm_ptr++ = (uint8_t)(((long)tm & 0x00ff0000) >> 16); + *tm_ptr++ = (uint8_t)(((long)tm & 0x0000ff00) >> 8); + *tm_ptr++ = (uint8_t)(((long)tm & 0x000000ff)); + get_random(SSL_RANDOM_SIZE-4, &buf[10]); + os_memcpy(ssl->dc->client_random, &buf[6], SSL_RANDOM_SIZE); + offset = 6 + SSL_RANDOM_SIZE; + + /* give session resumption a go */ + if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME)) /* set initially by user */ + { + buf[offset++] = ssl->sess_id_size; + os_memcpy(&buf[offset], ssl->session_id, ssl->sess_id_size); + offset += ssl->sess_id_size; + CLR_SSL_FLAG(SSL_SESSION_RESUME); /* clear so we can set later */ + } + else + { + /* no session id - because no session resumption just yet */ + buf[offset++] = 0; + } + + buf[offset++] = 0; /* number of ciphers */ + buf[offset++] = NUM_PROTOCOLS*2;/* number of ciphers */ + + /* put all our supported protocols in our request */ + for (i = 0; i < NUM_PROTOCOLS; i++) + { + buf[offset++] = 0; /* cipher we are using */ + buf[offset++] = ssl_prot_prefs[i]; + } + + buf[offset++] = 1; /* no compression */ + buf[offset++] = 0; + buf[3] = offset - 4; /* handshake size */ + + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset); +} + +/* + * Process the server hello. + */ +static int ICACHE_FLASH_ATTR process_server_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + int pkt_size = ssl->bm_index; + int num_sessions = ssl->ssl_ctx->num_sessions; + uint8_t sess_id_size; + int offset, ret = SSL_OK; + + /* check that we are talking to a TLSv1 server */ + uint8_t version = (buf[4] << 4) + buf[5]; + if (version > SSL_PROTOCOL_VERSION_MAX) + { + version = SSL_PROTOCOL_VERSION_MAX; + } + else if (ssl->version < SSL_PROTOCOL_MIN_VERSION) + { + ret = SSL_ERROR_INVALID_VERSION; + //ssl_display_error(ret); + goto error; + } + + ssl->version = version; + + /* get the server random value */ + os_memcpy(ssl->dc->server_random, &buf[6], SSL_RANDOM_SIZE); + offset = 6 + SSL_RANDOM_SIZE; /* skip of session id size */ + sess_id_size = buf[offset++]; + + if (sess_id_size > SSL_SESSION_ID_SIZE) + { + ret = SSL_ERROR_INVALID_SESSION; + goto error; + } + + if (num_sessions) + { + ssl->session = ssl_session_update(num_sessions, + ssl->ssl_ctx->ssl_sessions, ssl, &buf[offset]); + os_memcpy(ssl->session->session_id, &buf[offset], sess_id_size); + + /* pad the rest with 0's */ + if (sess_id_size < SSL_SESSION_ID_SIZE) + { + os_memset(&ssl->session->session_id[sess_id_size], 0, + SSL_SESSION_ID_SIZE-sess_id_size); + } + } + + os_memcpy(ssl->session_id, &buf[offset], sess_id_size); + ssl->sess_id_size = sess_id_size; + offset += sess_id_size; + + /* get the real cipher we are using */ + ssl->cipher = buf[++offset]; + ssl->next_state = IS_SET_SSL_FLAG(SSL_SESSION_RESUME) ? + HS_FINISHED : HS_CERTIFICATE; + + offset++; // skip the compr + PARANOIA_CHECK(pkt_size, offset); + ssl->dc->bm_proc_index = offset+1; + +error: + return ret; +} + +/** + * Process the server hello done message. + */ +static int ICACHE_FLASH_ATTR process_server_hello_done(SSL *ssl) +{ + ssl->next_state = HS_FINISHED; + return SSL_OK; +} + +/* + * Send a client key exchange message. + */ +static int ICACHE_FLASH_ATTR send_client_key_xchg(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + uint8_t premaster_secret[SSL_SECRET_SIZE]; + int enc_secret_size = -1; + + buf[0] = HS_CLIENT_KEY_XCHG; + buf[1] = 0; + + premaster_secret[0] = 0x03; /* encode the version number */ + premaster_secret[1] = SSL_PROTOCOL_MINOR_VERSION; /* must be TLS 1.1 */ + get_random(SSL_SECRET_SIZE-2, &premaster_secret[2]); + //DISPLAY_RSA(ssl, ssl->x509_ctx->rsa_ctx); + + /* rsa_ctx->bi_ctx is not thread-safe */ + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + enc_secret_size = RSA_encrypt(ssl->x509_ctx->rsa_ctx, premaster_secret, + SSL_SECRET_SIZE, &buf[6], 0); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + buf[2] = (enc_secret_size + 2) >> 8; + buf[3] = (enc_secret_size + 2) & 0xff; + buf[4] = enc_secret_size >> 8; + buf[5] = enc_secret_size & 0xff; + + generate_master_secret(ssl, premaster_secret); + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, enc_secret_size+6); +} + +/* + * Process the certificate request. + */ +static int ICACHE_FLASH_ATTR process_cert_req(SSL *ssl) +{ + uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index]; + int ret = SSL_OK; + int offset = (buf[2] << 4) + buf[3]; + int pkt_size = ssl->bm_index; + + /* don't do any processing - we will send back an RSA certificate anyway */ + ssl->next_state = HS_SERVER_HELLO_DONE; + SET_SSL_FLAG(SSL_HAS_CERT_REQ); + ssl->dc->bm_proc_index += offset; + PARANOIA_CHECK(pkt_size, offset); +error: + return ret; +} + +/* + * Send a certificate verify message. + */ +static int ICACHE_FLASH_ATTR send_cert_verify(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + uint8_t dgst[MD5_SIZE+SHA1_SIZE]; + RSA_CTX *rsa_ctx = ssl->ssl_ctx->rsa_ctx; + int n = 0, ret; + + //DISPLAY_RSA(ssl, rsa_ctx); + + buf[0] = HS_CERT_VERIFY; + buf[1] = 0; + + finished_digest(ssl, NULL, dgst); /* calculate the digest */ + + /* rsa_ctx->bi_ctx is not thread-safe */ + if (rsa_ctx) + { + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + n = RSA_encrypt(rsa_ctx, dgst, sizeof(dgst), &buf[6], 1); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + if (n == 0) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + } + + buf[4] = n >> 8; /* add the RSA size (not officially documented) */ + buf[5] = n & 0xff; + n += 2; + buf[2] = n >> 8; + buf[3] = n & 0xff; + ret = send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, n+4); + +error: + return ret; +} + +#endif /* CONFIG_SSL_ENABLE_CLIENT */ diff --git a/app/ssl/ssl/ssl_tls1_svr.c b/app/ssl/ssl/ssl_tls1_svr.c new file mode 100644 index 00000000..d82a6689 --- /dev/null +++ b/app/ssl/ssl/ssl_tls1_svr.c @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//#include +//#include +//#include +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_ssl.h" +//#include "../httpd/axhttp.h" +#include "ssl/app/espconn_ssl.h" + +static const uint8_t g_hello_done[] = { HS_SERVER_HELLO_DONE, 0, 0, 0 }; + +static int process_client_hello(SSL *ssl); +static int send_server_hello_sequence(SSL *ssl); +static int send_server_hello(SSL *ssl); +static int send_server_hello_done(SSL *ssl); +static int process_client_key_xchg(SSL *ssl); +#ifdef CONFIG_SSL_CERT_VERIFICATION +static int send_certificate_request(SSL *ssl); +static int process_cert_verify(SSL *ssl); +#endif + +#if 0 +/* + * Establish a new SSL connection to an SSL client. + */ +EXP_FUNC SSL * STDCALL ICACHE_FLASH_ATTR ssl_server_new(SSL_CTX *ssl_ctx, int client_fd) +{ + SSL *ssl; + + ssl = ssl_new(ssl_ctx, client_fd); + ssl->next_state = HS_CLIENT_HELLO; + +#ifdef CONFIG_SSL_FULL_MODE + if (ssl_ctx->chain_length == 0) + ssl_printf("Warning - no server certificate defined\n"); TTY_FLUSH(); +#endif + + return ssl; +} +#endif + +/* + * Establish a new SSL connection to an SSL client.(raw api)add by ives 12.19.2013 + */ +EXP_FUNC SSL *STDCALL ICACHE_FLASH_ATTR sslserver_new(SSL_CTX *ssl_ctx, struct tcp_pcb* client_pcb) +{ + SSL *ssl; + ssl = ssl_new_context(ssl_ctx, client_pcb); + ssl->next_state = HS_CLIENT_HELLO; +#ifdef CONFIG_SSL_FULL_MODE + if (ssl_ctx->chain_length == 0) + ssl_printf("Warning - no server certificate defined\n"); + //TTY_FLUSH(); +#endif + return ssl; +} +/* + * Process the handshake record. + */ +int ICACHE_FLASH_ATTR do_svr_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len) +{ + int ret = SSL_OK; + ssl->hs_status = SSL_NOT_OK; /* not connected */ + + /* To get here the state must be valid */ +// ssl_printf("%d %s %d\n",handshake_type, __func__, __LINE__); + switch (handshake_type) + { + case HS_CLIENT_HELLO: + if ((ret = process_client_hello(ssl)) == SSL_OK) + ret = send_server_hello_sequence(ssl); + break; + +#ifdef CONFIG_SSL_CERT_VERIFICATION + case HS_CERTIFICATE:/* the client sends its cert */ + ret = process_certificate(ssl, &ssl->x509_ctx); + + if (ret == SSL_OK) /* verify the cert */ + { + int cert_res; + cert_res = x509_verify( + ssl->ssl_ctx->ca_cert_ctx, ssl->x509_ctx); + ret = (cert_res == 0) ? SSL_OK : SSL_X509_ERROR(cert_res); + } + break; + + case HS_CERT_VERIFY: + ret = process_cert_verify(ssl); + add_packet(ssl, buf, hs_len); /* needs to be done after */ + break; +#endif + case HS_CLIENT_KEY_XCHG: + ret = process_client_key_xchg(ssl); + break; + + case HS_FINISHED: + ret = process_finished(ssl, buf, hs_len); + disposable_free(ssl); /* free up some memory */ + break; + } + + return ret; +} + +/* + * Process a client hello message. + */ +static int ICACHE_FLASH_ATTR process_client_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + uint8_t *record_buf = ssl->hmac_header; + int pkt_size = ssl->bm_index; + int i, j, cs_len, id_len, offset = 6 + SSL_RANDOM_SIZE; + int ret = SSL_OK; + + uint8_t version = (buf[4] << 4) + buf[5]; + ssl->version = ssl->client_version = version; + + if (version > SSL_PROTOCOL_VERSION_MAX) + { + /* use client's version instead */ + ssl->version = SSL_PROTOCOL_VERSION_MAX; + } + else if (version < SSL_PROTOCOL_MIN_VERSION) /* old version supported? */ + { + ret = SSL_ERROR_INVALID_VERSION; + //ssl_display_error(ret); + goto error; + } + + os_memcpy(ssl->dc->client_random, &buf[6], SSL_RANDOM_SIZE); + + /* process the session id */ + id_len = buf[offset++]; + if (id_len > SSL_SESSION_ID_SIZE) + { + return SSL_ERROR_INVALID_SESSION; + } + +#ifndef CONFIG_SSL_SKELETON_MODE + ssl->session = ssl_session_update(ssl->ssl_ctx->num_sessions, + ssl->ssl_ctx->ssl_sessions, ssl, id_len ? &buf[offset] : NULL); +#endif + + offset += id_len; + cs_len = (buf[offset]<<8) + buf[offset+1]; + offset += 2; /* add 1 due to all cipher suites being 8 bit */ + + PARANOIA_CHECK(pkt_size, offset); + + /* work out what cipher suite we are going to use - client defines + the preference */ + for (i = 0; i < cs_len; i += 2) + { + for (j = 0; j < NUM_PROTOCOLS; j++) + { + if (ssl_prot_prefs[j] == ((buf[offset+i]<<8) + buf[offset+i+1])) /* got a match? */ + { + ssl->cipher = ssl_prot_prefs[j]; + goto do_state; + } + } + } + + /* ouch! protocol is not supported */ + ret = SSL_ERROR_NO_CIPHER; + +do_state: +error: + return ret; +} + +#ifdef CONFIG_SSL_ENABLE_V23_HANDSHAKE +/* + * Some browsers use a hybrid SSLv2 "client hello" + */ +int process_sslv23_client_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + int bytes_needed = ((buf[0] & 0x7f) << 8) + buf[1]; + int ret = SSL_OK; + + /* we have already read 3 extra bytes so far */ +// int read_len = SOCKET_READ(ssl->client_fd, buf, bytes_needed-3); + int read_len = pbuf_copy_partial(ssl->ssl_pbuf, buf, bytes_needed - 3, 0); + int cs_len = buf[1]; + int id_len = buf[3]; + int ch_len = buf[5]; + int i, j, offset = 8; /* start at first cipher */ + int random_offset = 0; + + DISPLAY_BYTES(ssl, "received %d bytes", buf, read_len, read_len); + + add_packet(ssl, buf, read_len); + + /* connection has gone, so die */ + if (bytes_needed < 0) + { + return SSL_ERROR_CONN_LOST; + } + + /* now work out what cipher suite we are going to use */ + for (j = 0; j < NUM_PROTOCOLS; j++) + { + for (i = 0; i < cs_len; i += 3) + { + if (ssl_prot_prefs[j] == buf[offset+i]) + { + ssl->cipher = ssl_prot_prefs[j]; + goto server_hello; + } + } + } + + /* ouch! protocol is not supported */ + ret = SSL_ERROR_NO_CIPHER; + goto error; + +server_hello: + /* get the session id */ + offset += cs_len - 2; /* we've gone 2 bytes past the end */ +#ifndef CONFIG_SSL_SKELETON_MODE + ssl->session = ssl_session_update(ssl->ssl_ctx->num_sessions, + ssl->ssl_ctx->ssl_sessions, ssl, id_len ? &buf[offset] : NULL); +#endif + + /* get the client random data */ + offset += id_len; + + /* random can be anywhere between 16 and 32 bytes long - so it is padded + * with 0's to the left */ + if (ch_len == 0x10) + { + random_offset += 0x10; + } + + memcpy(&ssl->dc->client_random[random_offset], &buf[offset], ch_len); + ret = send_server_hello_sequence(ssl); + +error: + return ret; +} +#endif + +/* + * Send the entire server hello sequence + */ +static int ICACHE_FLASH_ATTR send_server_hello_sequence(SSL *ssl) +{ + int ret; + + if ((ret = send_server_hello(ssl)) == SSL_OK) + { +#ifndef CONFIG_SSL_SKELETON_MODE + /* resume handshake? */ + if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME)) + { + if ((ret = send_change_cipher_spec(ssl)) == SSL_OK) + { + ret = send_finished(ssl); + ssl->next_state = HS_FINISHED; + } + } + else +#endif + if ((ret = send_certificate(ssl)) == SSL_OK) + { +#ifdef CONFIG_SSL_CERT_VERIFICATION + /* ask the client for its certificate */ + if (IS_SET_SSL_FLAG(SSL_CLIENT_AUTHENTICATION)) + { + if ((ret = send_certificate_request(ssl)) == SSL_OK) + { + ret = send_server_hello_done(ssl); + ssl->next_state = HS_CERTIFICATE; + } + } + else +#endif + { + ret = send_server_hello_done(ssl); + ssl->next_state = HS_CLIENT_KEY_XCHG; + } + } + } + + return ret; +} + +/* + * Send a server hello message. + */ +static int ICACHE_FLASH_ATTR send_server_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + int offset = 0; + + buf[0] = HS_SERVER_HELLO; + buf[1] = 0; + buf[2] = 0; + /* byte 3 is calculated later */ + buf[4] = 0x03; + buf[5] = ssl->version & 0x0f; + + /* server random value */ + get_random(SSL_RANDOM_SIZE, &buf[6]); + os_memcpy(ssl->dc->server_random, &buf[6], SSL_RANDOM_SIZE); + offset = 6 + SSL_RANDOM_SIZE; + +#ifndef CONFIG_SSL_SKELETON_MODE + if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME)) + { + /* retrieve id from session cache */ + buf[offset++] = SSL_SESSION_ID_SIZE; + os_memcpy(&buf[offset], ssl->session->session_id, SSL_SESSION_ID_SIZE); + os_memcpy(ssl->session_id, ssl->session->session_id, SSL_SESSION_ID_SIZE); + ssl->sess_id_size = SSL_SESSION_ID_SIZE; + offset += SSL_SESSION_ID_SIZE; + } + else /* generate our own session id */ +#endif + { +#ifndef CONFIG_SSL_SKELETON_MODE + buf[offset++] = SSL_SESSION_ID_SIZE; + get_random(SSL_SESSION_ID_SIZE, &buf[offset]); + os_memcpy(ssl->session_id, &buf[offset], SSL_SESSION_ID_SIZE); + ssl->sess_id_size = SSL_SESSION_ID_SIZE; + + /* store id in session cache */ + if (ssl->ssl_ctx->num_sessions) + { + os_memcpy(ssl->session->session_id, + ssl->session_id, SSL_SESSION_ID_SIZE); + } + + offset += SSL_SESSION_ID_SIZE; +#else + buf[offset++] = 0; /* don't bother with session id in skelton mode */ +#endif + } + + buf[offset++] = 0; /* cipher we are using */ + buf[offset++] = ssl->cipher; + buf[offset++] = 0; /* no compression */ + buf[3] = offset - 4; /* handshake size */ + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset); +} + +/* + * Send the server hello done message. + */ +static int ICACHE_FLASH_ATTR send_server_hello_done(SSL *ssl) +{ + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, + g_hello_done, sizeof(g_hello_done)); +} + +/* + * Pull apart a client key exchange message. Decrypt the pre-master key (using + * our RSA private key) and then work out the master key. Initialise the + * ciphers. + */ +static int ICACHE_FLASH_ATTR process_client_key_xchg(SSL *ssl) +{ + uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index]; + int pkt_size = ssl->bm_index; + int premaster_size, secret_length = (buf[2] << 8) + buf[3]; + uint8_t premaster_secret[MAX_KEY_BYTE_SIZE]; + RSA_CTX *rsa_ctx = ssl->ssl_ctx->rsa_ctx; + int offset = 4; + int ret = SSL_OK; + + if (rsa_ctx == NULL) + { + ret = SSL_ERROR_NO_CERT_DEFINED; + goto error; + } + + /* is there an extra size field? */ + if ((secret_length - 2) == rsa_ctx->num_octets) + offset += 2; + + PARANOIA_CHECK(pkt_size, rsa_ctx->num_octets+offset); + + /* rsa_ctx->bi_ctx is not thread-safe */ + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + premaster_size = RSA_decrypt(rsa_ctx, &buf[offset], premaster_secret, 1); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + if (premaster_size != SSL_SECRET_SIZE || + premaster_secret[0] != 0x03 || /* must be the same as client + offered version */ + premaster_secret[1] != (ssl->client_version & 0x0f)) + { + /* guard against a Bleichenbacher attack */ + get_random(SSL_SECRET_SIZE, premaster_secret); + /* and continue - will die eventually when checking the mac */ + } + +#if 0 + print_blob("pre-master", premaster_secret, SSL_SECRET_SIZE); +#endif + + generate_master_secret(ssl, premaster_secret); + +#ifdef CONFIG_SSL_CERT_VERIFICATION + ssl->next_state = IS_SET_SSL_FLAG(SSL_CLIENT_AUTHENTICATION) ? + HS_CERT_VERIFY : HS_FINISHED; +#else + ssl->next_state = HS_FINISHED; +#endif + + ssl->dc->bm_proc_index += rsa_ctx->num_octets+offset; +error: + return ret; +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +static const uint8_t g_cert_request[] = { HS_CERT_REQ, 0, 0, 4, 1, 0, 0, 0 }; + +/* + * Send the certificate request message. + */ +static int ICACHE_FLASH_ATTR send_certificate_request(SSL *ssl) +{ + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, + g_cert_request, sizeof(g_cert_request)); +} + +/* + * Ensure the client has the private key by first decrypting the packet and + * then checking the packet digests. + */ +static int ICACHE_FLASH_ATTR process_cert_verify(SSL *ssl) +{ + uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index]; + int pkt_size = ssl->bm_index; + uint8_t dgst_buf[MAX_KEY_BYTE_SIZE]; + uint8_t dgst[MD5_SIZE+SHA1_SIZE]; + X509_CTX *x509_ctx = ssl->x509_ctx; + int ret = SSL_OK; + int n; + + PARANOIA_CHECK(pkt_size, x509_ctx->rsa_ctx->num_octets+6); + //DISPLAY_RSA(ssl, x509_ctx->rsa_ctx); + + /* rsa_ctx->bi_ctx is not thread-safe */ + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + n = RSA_decrypt(x509_ctx->rsa_ctx, &buf[6], dgst_buf, 0); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + if (n != SHA1_SIZE + MD5_SIZE) + { + ret = SSL_ERROR_INVALID_KEY; + goto end_cert_vfy; + } + + finished_digest(ssl, NULL, dgst); /* calculate the digest */ + if (os_memcmp(dgst_buf, dgst, MD5_SIZE + SHA1_SIZE)) + { + ret = SSL_ERROR_INVALID_KEY; + } + +end_cert_vfy: + ssl->next_state = HS_FINISHED; +error: + return ret; +} + +#endif diff --git a/app/ssl/ssl/ssl_x509.c b/app/ssl/ssl/ssl_x509.c new file mode 100644 index 00000000..b480e873 --- /dev/null +++ b/app/ssl/ssl/ssl_x509.c @@ -0,0 +1,565 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file x509.c + * + * Certificate processing. + */ + +//#include +//#include +//#include +//#include +#include "ssl/app/espconn_ssl.h" + +#include "ssl/ssl_os_port.h" +#include "ssl/ssl_crypto_misc.h" +//#include "os.h" +#include "lwip/mem.h" + + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Retrieve the signature from a certificate. + */ +static const uint8_t * ICACHE_FLASH_ATTR get_signature(const uint8_t *asn1_sig, int *len) +{ + int offset = 0; + const uint8_t *ptr = NULL; + + if (asn1_next_obj(asn1_sig, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(asn1_sig, &offset, ASN1_SEQUENCE)) + goto end_get_sig; + + if (asn1_sig[offset++] != ASN1_OCTET_STRING) + goto end_get_sig; + *len = get_asn1_length(asn1_sig, &offset); + ptr = &asn1_sig[offset]; /* all ok */ + +end_get_sig: + return ptr; +} + +#endif + +/** + * Construct a new x509 object. + * @return 0 if ok. < 0 if there was a problem. + */ +int ICACHE_FLASH_ATTR x509_new(const uint8_t *cert, int *len, X509_CTX **ctx) +{ + int begin_tbs, end_tbs; + int ret = X509_NOT_OK, offset = 0, cert_size = 0; + X509_CTX *x509_ctx; + BI_CTX *bi_ctx; + + *ctx = (X509_CTX *)os_zalloc(sizeof(X509_CTX)); + x509_ctx = *ctx; + + /* get the certificate size */ + asn1_skip_obj(cert, &cert_size, ASN1_SEQUENCE); + + if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) + goto end_cert; + + begin_tbs = offset; /* start of the tbs */ + end_tbs = begin_tbs; /* work out the end of the tbs */ + asn1_skip_obj(cert, &end_tbs, ASN1_SEQUENCE); + + if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) + goto end_cert; + + if (cert[offset] == ASN1_EXPLICIT_TAG) /* optional version */ + { + if (asn1_version(cert, &offset, x509_ctx)) + goto end_cert; + } + + if (asn1_skip_obj(cert, &offset, ASN1_INTEGER) || /* serial number */ + asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) + goto end_cert; + + /* make sure the signature is ok */ + if (asn1_signature_type(cert, &offset, x509_ctx)) + { + ret = X509_VFY_ERROR_UNSUPPORTED_DIGEST; + goto end_cert; + } + + if (asn1_name(cert, &offset, x509_ctx->ca_cert_dn) || + asn1_validity(cert, &offset, x509_ctx) || + asn1_name(cert, &offset, x509_ctx->cert_dn) || + asn1_public_key(cert, &offset, x509_ctx)) + { + goto end_cert; + } + + bi_ctx = x509_ctx->rsa_ctx->bi_ctx; +#ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */ + /* use the appropriate signature algorithm (SHA1/MD5/MD2) */ + if (x509_ctx->sig_type == SIG_TYPE_MD5) + { + MD5_CTX md5_ctx; + uint8_t md5_dgst[MD5_SIZE]; + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, &cert[begin_tbs], end_tbs-begin_tbs); + MD5_Final(md5_dgst, &md5_ctx); + x509_ctx->digest = bi_import(bi_ctx, md5_dgst, MD5_SIZE); + } + else if (x509_ctx->sig_type == SIG_TYPE_SHA1) + { + SHA1_CTX sha_ctx; + uint8_t sha_dgst[SHA1_SIZE]; + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, &cert[begin_tbs], end_tbs-begin_tbs); + SHA1_Final(sha_dgst, &sha_ctx); + x509_ctx->digest = bi_import(bi_ctx, sha_dgst, SHA1_SIZE); + } + else if (x509_ctx->sig_type == SIG_TYPE_MD2) + { + MD2_CTX md2_ctx; + uint8_t md2_dgst[MD2_SIZE]; + MD2_Init(&md2_ctx); + MD2_Update(&md2_ctx, &cert[begin_tbs], end_tbs-begin_tbs); + MD2_Final(md2_dgst, &md2_ctx); + x509_ctx->digest = bi_import(bi_ctx, md2_dgst, MD2_SIZE); + } + + if (cert[offset] == ASN1_V3_DATA) + { + int suboffset; + + ++offset; + get_asn1_length(cert, &offset); + + if ((suboffset = asn1_find_subjectaltname(cert, offset)) > 0) + { + if (asn1_next_obj(cert, &suboffset, ASN1_OCTET_STRING) > 0) + { + int altlen; + + if ((altlen = asn1_next_obj(cert, + &suboffset, ASN1_SEQUENCE)) > 0) + { + int endalt = suboffset + altlen; + int totalnames = 0; + + while (suboffset < endalt) + { + int type = cert[suboffset++]; + int dnslen = get_asn1_length(cert, &suboffset); + + if (type == ASN1_CONTEXT_DNSNAME) + { + x509_ctx->subject_alt_dnsnames = (char**) + os_realloc(x509_ctx->subject_alt_dnsnames, + (totalnames + 2) * sizeof(char*)); + x509_ctx->subject_alt_dnsnames[totalnames] = + (char*)os_malloc(dnslen + 1); + x509_ctx->subject_alt_dnsnames[totalnames+1] = NULL; + os_memcpy(x509_ctx->subject_alt_dnsnames[totalnames], + cert + suboffset, dnslen); + x509_ctx->subject_alt_dnsnames[ + totalnames][dnslen] = 0; + ++totalnames; + } + + suboffset += dnslen; + } + } + } + } + } + + offset = end_tbs; /* skip the rest of v3 data */ + if (asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) || + asn1_signature(cert, &offset, x509_ctx)) + goto end_cert; +#endif + ret = X509_OK; +end_cert: + if (len) + { + *len = cert_size; + } + + if (ret) + { +#ifdef CONFIG_SSL_FULL_MODE + ssl_printf("Error: Invalid X509 ASN.1 file (%s)\n", + x509_display_error(ret)); +#endif + x509_free(x509_ctx); + *ctx = NULL; + } + + return ret; +} + +/** + * Free an X.509 object's resources. + */ +void ICACHE_FLASH_ATTR x509_free(X509_CTX *x509_ctx) +{ + X509_CTX *next; + int i; + + if (x509_ctx == NULL) /* if already null, then don't bother */ + return; + + for (i = 0; i < X509_NUM_DN_TYPES; i++) + { + os_free(x509_ctx->ca_cert_dn[i]); + os_free(x509_ctx->cert_dn[i]); + } + + os_free(x509_ctx->signature); + +#ifdef CONFIG_SSL_CERT_VERIFICATION + if (x509_ctx->digest) + { + bi_free(x509_ctx->rsa_ctx->bi_ctx, x509_ctx->digest); + } + + if (x509_ctx->subject_alt_dnsnames) + { + for (i = 0; x509_ctx->subject_alt_dnsnames[i]; ++i) + os_free(x509_ctx->subject_alt_dnsnames[i]); + + os_free(x509_ctx->subject_alt_dnsnames); + } +#endif + + RSA_free(x509_ctx->rsa_ctx); + next = x509_ctx->next; + os_free(x509_ctx); + x509_free(next); /* clear the chain */ +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Take a signature and decrypt it. + */ +static bigint *ICACHE_FLASH_ATTR sig_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, + bigint *modulus, bigint *pub_exp) +{ + int i, size; + bigint *decrypted_bi, *dat_bi; + bigint *bir = NULL; + uint8_t *block = (uint8_t *)os_malloc(sig_len); + + /* decrypt */ + dat_bi = bi_import(ctx, sig, sig_len); + ctx->mod_offset = BIGINT_M_OFFSET; + + /* convert to a normal block */ + decrypted_bi = bi_mod_power2(ctx, dat_bi, modulus, pub_exp); + + bi_export(ctx, decrypted_bi, block, sig_len); + ctx->mod_offset = BIGINT_M_OFFSET; + + i = 10; /* start at the first possible non-padded byte */ + while (block[i++] && i < sig_len); + size = sig_len - i; + + /* get only the bit we want */ + if (size > 0) + { + int len; + const uint8_t *sig_ptr = get_signature(&block[i], &len); + + if (sig_ptr) + { + bir = bi_import(ctx, sig_ptr, len); + } + } + + /* save a few bytes of memory */ + bi_clear_cache(ctx); + + os_free(block); + return bir; +} + +/** + * Do some basic checks on the certificate chain. + * + * Certificate verification consists of a number of checks: + * - The date of the certificate is after the start date. + * - The date of the certificate is before the finish date. + * - A root certificate exists in the certificate store. + * - That the certificate(s) are not self-signed. + * - The certificate chain is valid. + * - The signature of the certificate is valid. + */ +int ICACHE_FLASH_ATTR x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) +{ + int ret = X509_OK, i = 0; + bigint *cert_sig; + X509_CTX *next_cert = NULL; + BI_CTX *ctx = NULL; + bigint *mod = NULL, *expn = NULL; + int match_ca_cert = 0; + struct timeval tv; + uint8_t is_self_signed = 0; + + if (cert == NULL) + { + ret = X509_VFY_ERROR_NO_TRUSTED_CERT; + goto end_verify; + } + + /* a self-signed certificate that is not in the CA store - use this + to check the signature */ + if (asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0) + { + is_self_signed = 1; + ctx = cert->rsa_ctx->bi_ctx; + mod = cert->rsa_ctx->m; + expn = cert->rsa_ctx->e; + } + +// gettimeofday(&tv, NULL); + + /* check the not before date */ + if (tv.tv_sec < cert->not_before) + { + ret = X509_VFY_ERROR_NOT_YET_VALID; + goto end_verify; + } + + /* check the not after date */ + if (tv.tv_sec > cert->not_after) + { + ret = X509_VFY_ERROR_EXPIRED; + goto end_verify; + } + + next_cert = cert->next; + + /* last cert in the chain - look for a trusted cert */ + if (next_cert == NULL) + { + if (ca_cert_ctx != NULL) + { + /* go thu the CA store */ + while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) + { + if (asn1_compare_dn(cert->ca_cert_dn, + ca_cert_ctx->cert[i]->cert_dn) == 0) + { + /* use this CA certificate for signature verification */ + match_ca_cert = 1; + ctx = ca_cert_ctx->cert[i]->rsa_ctx->bi_ctx; + mod = ca_cert_ctx->cert[i]->rsa_ctx->m; + expn = ca_cert_ctx->cert[i]->rsa_ctx->e; + break; + } + + i++; + } + } + + /* couldn't find a trusted cert (& let self-signed errors + be returned) */ + if (!match_ca_cert && !is_self_signed) + { + ret = X509_VFY_ERROR_NO_TRUSTED_CERT; + goto end_verify; + } + } + else if (asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn) != 0) + { + /* check the chain */ + ret = X509_VFY_ERROR_INVALID_CHAIN; + goto end_verify; + } + else /* use the next certificate in the chain for signature verify */ + { + ctx = next_cert->rsa_ctx->bi_ctx; + mod = next_cert->rsa_ctx->m; + expn = next_cert->rsa_ctx->e; + } + + /* cert is self signed */ + if (!match_ca_cert && is_self_signed) + { + ret = X509_VFY_ERROR_SELF_SIGNED; + goto end_verify; + } + + /* check the signature */ + cert_sig = sig_verify(ctx, cert->signature, cert->sig_len, + bi_clone(ctx, mod), bi_clone(ctx, expn)); + + if (cert_sig && cert->digest) + { + if (bi_compare(cert_sig, cert->digest) != 0) + ret = X509_VFY_ERROR_BAD_SIGNATURE; + + + bi_free(ctx, cert_sig); + } + else + { + ret = X509_VFY_ERROR_BAD_SIGNATURE; + } + + if (ret) + goto end_verify; + + /* go down the certificate chain using recursion. */ + if (next_cert != NULL) + { + ret = x509_verify(ca_cert_ctx, next_cert); + } + +end_verify: + return ret; +} +#endif + +#if defined (CONFIG_SSL_FULL_MODE) +/** + * Used for diagnostics. + */ +static const char *not_part_of_cert = ""; +void ICACHE_FLASH_ATTR x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx) +{ + if (cert == NULL) + return; + + ssl_printf("=== CERTIFICATE ISSUED TO ===\n"); + ssl_printf("Common Name (CN):\t\t"); + ssl_printf("%s\n", cert->cert_dn[X509_COMMON_NAME] ? + cert->cert_dn[X509_COMMON_NAME] : not_part_of_cert); + + ssl_printf("Organization (O):\t\t"); + ssl_printf("%s\n", cert->cert_dn[X509_ORGANIZATION] ? + cert->cert_dn[X509_ORGANIZATION] : not_part_of_cert); + + ssl_printf("Organizational Unit (OU):\t"); + ssl_printf("%s\n", cert->cert_dn[X509_ORGANIZATIONAL_UNIT] ? + cert->cert_dn[X509_ORGANIZATIONAL_UNIT] : not_part_of_cert); + + ssl_printf("=== CERTIFICATE ISSUED BY ===\n"); + ssl_printf("Common Name (CN):\t\t"); + ssl_printf("%s\n", cert->ca_cert_dn[X509_COMMON_NAME] ? + cert->ca_cert_dn[X509_COMMON_NAME] : not_part_of_cert); + + ssl_printf("Organization (O):\t\t"); + ssl_printf("%s\n", cert->ca_cert_dn[X509_ORGANIZATION] ? + cert->ca_cert_dn[X509_ORGANIZATION] : not_part_of_cert); + + ssl_printf("Organizational Unit (OU):\t"); + ssl_printf("%s\n", cert->ca_cert_dn[X509_ORGANIZATIONAL_UNIT] ? + cert->ca_cert_dn[X509_ORGANIZATIONAL_UNIT] : not_part_of_cert); + + ssl_printf("Not Before:\t\t\t%d\n", cert->not_before); + ssl_printf("Not After:\t\t\t%d\n", cert->not_after); + ssl_printf("RSA bitsize:\t\t\t%d\n", cert->rsa_ctx->num_octets*8); + ssl_printf("Sig Type:\t\t\t"); + switch (cert->sig_type) + { + case SIG_TYPE_MD5: + ssl_printf("MD5\n"); + break; + case SIG_TYPE_SHA1: + ssl_printf("SHA1\n"); + break; + case SIG_TYPE_MD2: + ssl_printf("MD2\n"); + break; + default: + ssl_printf("Unrecognized: %d\n", cert->sig_type); + break; + } + + if (ca_cert_ctx) + { + ssl_printf("Verify:\t\t\t\t%s\n", + x509_display_error(x509_verify(ca_cert_ctx, cert))); + } + +#if 0 + print_blob("Signature", cert->signature, cert->sig_len); + bi_print("Modulus", cert->rsa_ctx->m); + bi_print("Pub Exp", cert->rsa_ctx->e); +#endif + + if (ca_cert_ctx) + { + x509_print(cert->next, ca_cert_ctx); + } + + //TTY_FLUSH(); +} + +const char * ICACHE_FLASH_ATTR x509_display_error(int error) +{ + switch (error) + { + case X509_OK: + return "Certificate verify successful"; + + case X509_NOT_OK: + return "X509 not ok"; + + case X509_VFY_ERROR_NO_TRUSTED_CERT: + return "No trusted cert is available"; + + case X509_VFY_ERROR_BAD_SIGNATURE: + return "Bad signature"; + + case X509_VFY_ERROR_NOT_YET_VALID: + return "Cert is not yet valid"; + + case X509_VFY_ERROR_EXPIRED: + return "Cert has expired"; + + case X509_VFY_ERROR_SELF_SIGNED: + return "Cert is self-signed"; + + case X509_VFY_ERROR_INVALID_CHAIN: + return "Chain is invalid (check order of certs)"; + + case X509_VFY_ERROR_UNSUPPORTED_DIGEST: + return "Unsupported digest"; + + case X509_INVALID_PRIV_KEY: + return "Invalid private key"; + + default: + return "Unknown"; + } +} +#endif /* CONFIG_SSL_FULL_MODE */ + diff --git a/app/upgrade/Makefile b/app/upgrade/Makefile new file mode 100644 index 00000000..3ea087c2 --- /dev/null +++ b/app/upgrade/Makefile @@ -0,0 +1,47 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = libupgrade.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ../../include/ets +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/upgrade/upgrade.c b/app/upgrade/upgrade.c new file mode 100644 index 00000000..321a7328 --- /dev/null +++ b/app/upgrade/upgrade.c @@ -0,0 +1,317 @@ +#include "ets_sys.h" +#include "osapi.h" +#include "os_type.h" + +#include "lwip/err.h" +#include "lwip/ip_addr.h" +#include "lwip/mem.h" +#include "lwip/app/espconn.h" + +#include "upgrade.h" + +#include "upgrade_lib.c" + +#define UPGRADE_DEBUG +#ifdef UPGRADE_DEBUG +#define UPGRADE_DBG os_printf +#else +#define UPGRADE_DBG +#endif + +LOCAL struct espconn *upgrade_conn; +LOCAL uint8 *pbuf; +LOCAL os_timer_t upgrade_10s; +LOCAL os_timer_t upgrade_timer; +LOCAL uint32 totallength = 0; +LOCAL uint32 sumlength = 0; + +/****************************************************************************** + * FunctionName : upgrade_disconcb + * Description : The connection has been disconnected successfully. + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +upgrade_disconcb(void *arg) +{ + struct espconn *pespconn = arg; + + if (pespconn == NULL) { + return; + } + + os_free(pespconn->proto.tcp); + pespconn->proto.tcp = NULL; + os_free(pespconn); + pespconn = NULL; + upgrade_conn = NULL; +} + +/****************************************************************************** + * FunctionName : upgrade_datasent + * Description : Data has been sent successfully,This means that more data can + * be sent. + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +upgrade_datasent(void *arg) +{ + struct espconn *pespconn = arg; + + if (pespconn ->state == ESPCONN_CONNECT) { + } +} + +/****************************************************************************** + * FunctionName : upgrade_deinit + * Description : disconnect the connection with the host + * Parameters : bin -- server number + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +LOCAL upgrade_deinit(void) +{ + if (system_upgrade_flag_check() != UPGRADE_FLAG_START) { + system_upgrade_deinit(); + } + + +} + +/****************************************************************************** + * FunctionName : upgrade_10s_cb + * Description : Processing the client when connected with host time out + * Parameters : pespconn -- A point to the host + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR upgrade_10s_cb(struct espconn *pespconn) +{ + if (pespconn == NULL) { + return; + } + + system_upgrade_deinit(); + os_free(pespconn->proto.tcp); + pespconn->proto.tcp = NULL; + os_free(pespconn); + pespconn = NULL; + upgrade_conn = NULL; +} + +/****************************************************************************** + * FunctionName : user_upgrade_check + * 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 +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +upgrade_check(struct upgrade_server_info *server) +{ + UPGRADE_DBG("upgrade_check\n"); + + if (system_upgrade_flag_check() != UPGRADE_FLAG_FINISH) { + totallength = 0; + sumlength = 0; + os_timer_disarm(&upgrade_timer); + system_upgrade_flag_set(UPGRADE_FLAG_IDLE); + upgrade_deinit(); + server->upgrade_flag = false; + + if (server->check_cb != NULL) { + server->check_cb(server); + } + } else { + os_timer_disarm(&upgrade_timer); + upgrade_deinit(); + server->upgrade_flag = true; + + if (server->check_cb != NULL) { + server->check_cb(server); + } + } +#ifdef UPGRADE_SSL_ENABLE + espconn_secure_disconnect(upgrade_conn); +#else + espconn_disconnect(upgrade_conn); + +#endif +} + +/****************************************************************************** + * FunctionName : upgrade_download + * Description : Processing the upgrade data from the host + * Parameters : bin -- server number + * pusrdata -- The upgrade data (or NULL when the connection has been closed!) + * length -- The length of upgrade data + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +upgrade_download(void *arg, char *pusrdata, unsigned short length) +{ + char *ptr = NULL; + char *ptmp2 = NULL; + char lengthbuffer[32]; + if (totallength == 0 && (ptr = (char *)os_strstr(pusrdata, "\r\n\r\n")) != NULL && + (ptr = (char *)os_strstr(pusrdata, "Content-Length")) != NULL) { + ptr = (char *)os_strstr(pusrdata, "\r\n\r\n"); + length -= ptr - pusrdata; + length -= 4; + totallength += length; + UPGRADE_DBG("upgrade file download start.\n"); + system_upgrade(ptr + 4, length); + ptr = (char *)os_strstr(pusrdata, "Content-Length: "); + + if (ptr != NULL) { + ptr += 16; + ptmp2 = (char *)os_strstr(ptr, "\r\n"); + + if (ptmp2 != NULL) { + os_memset(lengthbuffer, 0, sizeof(lengthbuffer)); + os_memcpy(lengthbuffer, ptr, ptmp2 - ptr); + sumlength = atoi(lengthbuffer); + } else { + UPGRADE_DBG("sumlength failed\n"); + } + } else { + UPGRADE_DBG("Content-Length: failed\n"); + } + } else { + totallength += length; + os_printf("totallen = %d\n",totallength); + system_upgrade(pusrdata, length); + } + + if (totallength == sumlength) { + UPGRADE_DBG("upgrade file download finished.\n"); + system_upgrade_flag_set(UPGRADE_FLAG_FINISH); + totallength = 0; + sumlength = 0; + upgrade_check(upgrade_conn->reverse); + os_timer_disarm(&upgrade_10s); + os_timer_setfn(&upgrade_10s, (os_timer_func_t *)upgrade_deinit, NULL); + os_timer_arm(&upgrade_10s, 10, 0); + } else { + if (upgrade_conn->state != ESPCONN_READ) { + totallength = 0; + sumlength = 0; + os_timer_disarm(&upgrade_10s); + os_timer_setfn(&upgrade_10s, (os_timer_func_t *)upgrade_check, upgrade_conn->reverse); + os_timer_arm(&upgrade_10s, 10, 0); + } + } +} + +/****************************************************************************** + * FunctionName : upgrade_connect + * Description : client connected with a host successfully + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +upgrade_connect_cb(void *arg) +{ + struct espconn *pespconn = arg; + + UPGRADE_DBG("upgrade_connect_cb\n"); + os_timer_disarm(&upgrade_10s); + + espconn_regist_disconcb(pespconn, upgrade_disconcb); + espconn_regist_sentcb(pespconn, upgrade_datasent); + + if (pbuf != NULL) { + UPGRADE_DBG(pbuf); +#ifdef UPGRADE_SSL_ENABLE + espconn_secure_sent(pespconn, pbuf, os_strlen(pbuf)); +#else + espconn_sent(pespconn, pbuf, os_strlen(pbuf)); +#endif + } +} + +/****************************************************************************** + * FunctionName : upgrade_connection + * Description : connect with a server + * Parameters : bin -- server number + * url -- the url whitch upgrade files saved + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +upgrade_connect(struct upgrade_server_info *server) +{ + UPGRADE_DBG("upgrade_connect\n"); + + pbuf = server->url; + + espconn_regist_connectcb(upgrade_conn, upgrade_connect_cb); + espconn_regist_recvcb(upgrade_conn, upgrade_download); + + system_upgrade_init(); + system_upgrade_flag_set(UPGRADE_FLAG_START); + +#ifdef UPGRADE_SSL_ENABLE + espconn_secure_connect(upgrade_conn); +#else + espconn_connect(upgrade_conn); +#endif + + os_timer_disarm(&upgrade_10s); + os_timer_setfn(&upgrade_10s, (os_timer_func_t *)upgrade_10s_cb, upgrade_conn); + os_timer_arm(&upgrade_10s, 10000, 0); +} + +/****************************************************************************** + * FunctionName : user_upgrade_init + * Description : parameter initialize as a client + * Parameters : server -- A point to a server parmer which connected + * Returns : none +*******************************************************************************/ +bool ICACHE_FLASH_ATTR +#ifdef UPGRADE_SSL_ENABLE +system_upgrade_start_ssl(struct upgrade_server_info *server) +#else +system_upgrade_start(struct upgrade_server_info *server) +#endif +{ + if (system_upgrade_flag_check() == UPGRADE_FLAG_START) { + return false; + } + if (server == NULL) { + UPGRADE_DBG("server is NULL\n"); + return false; + } + if (upgrade_conn == NULL) { + upgrade_conn = (struct espconn *)os_zalloc(sizeof(struct espconn)); + } + + if (upgrade_conn != NULL) { + upgrade_conn->proto.tcp = NULL; + upgrade_conn->type = ESPCONN_TCP; + upgrade_conn->state = ESPCONN_NONE; + upgrade_conn->reverse = server; + + if (upgrade_conn->proto.tcp == NULL) { + upgrade_conn->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); + } + + if (upgrade_conn->proto.tcp != NULL) { + upgrade_conn->proto.tcp->local_port = espconn_port(); + upgrade_conn->proto.tcp->remote_port = server->port; + + os_memcpy(upgrade_conn->proto.tcp->remote_ip, server->ip, 4); + + UPGRADE_DBG("%s\n", __func__); + upgrade_connect(server); + + if (server->check_cb != NULL) { + os_timer_disarm(&upgrade_timer); + os_timer_setfn(&upgrade_timer, (os_timer_func_t *)upgrade_check, server); + os_timer_arm(&upgrade_timer, server->check_times, 0); + } + } + } + + return true; +} diff --git a/app/upgrade/upgrade_lib.c b/app/upgrade/upgrade_lib.c new file mode 100644 index 00000000..347209be --- /dev/null +++ b/app/upgrade/upgrade_lib.c @@ -0,0 +1,145 @@ +#include "ets_sys.h" +#include "spi_flash.h" + +//#include "net80211/ieee80211_var.h" +#include "lwip/mem.h" + +#include "upgrade.h" + +struct upgrade_param { + uint32 fw_bin_addr; + + uint8 fw_bin_sec; + uint8 fw_bin_sec_num; + + uint8 fw_bin_sec_earse; + + uint8 extra; + + uint8 save[4]; + + uint8 *buffer; +}; + +LOCAL struct upgrade_param *upgrade; + +extern SpiFlashChip *flashchip; + +/****************************************************************************** + * FunctionName : system_upgrade_internal + * Description : a + * Parameters : + * Returns : +*******************************************************************************/ +LOCAL bool ICACHE_FLASH_ATTR +system_upgrade_internal(struct upgrade_param *upgrade, uint8 *data, uint16 len) +{ + bool ret = false; + if(data == NULL || len == 0) + { + return true; + } + upgrade->buffer = (uint8 *)os_zalloc(len + upgrade->extra); + + os_memcpy(upgrade->buffer, upgrade->save, upgrade->extra); + os_memcpy(upgrade->buffer + upgrade->extra, data, len); + + len += upgrade->extra; + upgrade->extra = len & 0x03; + len -= upgrade->extra; + + os_memcpy(upgrade->save, upgrade->buffer + len, upgrade->extra); + + do { + if (upgrade->fw_bin_addr + len >= (upgrade->fw_bin_sec + upgrade->fw_bin_sec_num) * SPI_FLASH_SEC_SIZE) { + break; + } + + if (len > SPI_FLASH_SEC_SIZE) { + + } else { +// os_printf("%x %x\n",upgrade->fw_bin_sec_earse,upgrade->fw_bin_addr); + /* earse sector, just earse when first enter this zone */ + if (upgrade->fw_bin_sec_earse != (upgrade->fw_bin_addr + len) >> 12) { + upgrade->fw_bin_sec_earse = (upgrade->fw_bin_addr + len) >> 12; + spi_flash_erase_sector(upgrade->fw_bin_sec_earse); +// os_printf("%x\n",upgrade->fw_bin_sec_earse); + } + } + + if (spi_flash_write(upgrade->fw_bin_addr, (uint32 *)upgrade->buffer, len) != SPI_FLASH_RESULT_OK) { + break; + } + + ret = true; + upgrade->fw_bin_addr += len; + } while (0); + + os_free(upgrade->buffer); + upgrade->buffer = NULL; + return ret; +} + +/****************************************************************************** + * FunctionName : system_upgrade + * Description : a + * Parameters : + * Returns : +*******************************************************************************/ +bool ICACHE_FLASH_ATTR +system_upgrade(uint8 *data, uint16 len) +{ + bool ret; + + ret = system_upgrade_internal(upgrade, data, len); + + return ret; +} + +/****************************************************************************** + * FunctionName : system_upgrade_init + * Description : a + * Parameters : + * Returns : +*******************************************************************************/ +void ICACHE_FLASH_ATTR +system_upgrade_init(void) +{ + uint32 user_bin2_start; + uint8 flash_buf[4]; + uint8 high_half; + + spi_flash_read(0, (uint32 *)flash_buf, 4); + high_half = (flash_buf[3] & 0xF0) >> 4; + + if (upgrade == NULL) { + upgrade = (struct upgrade_param *)os_zalloc(sizeof(struct upgrade_param)); + } + + system_upgrade_flag_set(UPGRADE_FLAG_IDLE); + + if (high_half == 2 || high_half == 3 || high_half == 4) { + user_bin2_start = 129; // 128 + 1 + upgrade->fw_bin_sec_num = 123; // 128 - 1 - 4 + } else { + user_bin2_start = 65; // 64 + 1 + upgrade->fw_bin_sec_num = 59; // 64 - 1 - 4 + } + + upgrade->fw_bin_sec = (system_upgrade_userbin_check() == USER_BIN1) ? user_bin2_start : 1; + + upgrade->fw_bin_addr = upgrade->fw_bin_sec * SPI_FLASH_SEC_SIZE; +} + +/****************************************************************************** + * FunctionName : system_upgrade_deinit + * Description : a + * Parameters : + * Returns : +*******************************************************************************/ +void ICACHE_FLASH_ATTR +system_upgrade_deinit(void) +{ + os_free(upgrade); + upgrade = NULL; +} diff --git a/app/user/Makefile b/app/user/Makefile new file mode 100644 index 00000000..0dd1afe6 --- /dev/null +++ b/app/user/Makefile @@ -0,0 +1,49 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +GEN_LIBS = libuser.a +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ../../include/ets +INCLUDES += -I ../libc +INCLUDES += -I ../platform +INCLUDES += -I ../lua +INCLUDES += -I ../wofs +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/user/user_main.c b/app/user/user_main.c new file mode 100644 index 00000000..13e727bf --- /dev/null +++ b/app/user/user_main.c @@ -0,0 +1,110 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: user_main.c + * + * Description: entry file of user application + * + * Modification history: + * 2014/1/1, v1.0 create this file. +*******************************************************************************/ +#include "lua.h" +#include "platform.h" +#include "c_string.h" +#include "c_stdlib.h" +#include "c_stdio.h" + +#include "romfs.h" + +#include "user_interface.h" + +#include "ets_sys.h" +#include "driver/uart.h" +#include "mem.h" + +#define SIG_LUA 0 +#define TASK_QUEUE_LEN 4 +os_event_t *taskQueue; + +void task_lua(os_event_t *e){ + char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL }; + NODE_DBG("Task task_lua started.\n"); + switch(e->sig){ + case SIG_LUA: + NODE_DBG("SIG_LUA received.\n"); + lua_main( 2, lua_argv ); + break; + default: + break; + } +} + +void task_init(void){ + taskQueue = (os_event_t *)os_malloc(sizeof(os_event_t) * TASK_QUEUE_LEN); + system_os_task(task_lua, USER_TASK_PRIO_0, taskQueue, TASK_QUEUE_LEN); +} + +extern void spiffs_mount(); +// extern void test_spiffs(); +// extern int test_romfs(); + +/****************************************************************************** + * FunctionName : user_init + * Description : entry of user application, init user function here + * Parameters : none + * Returns : none +*******************************************************************************/ +void user_init(void) +{ + // NODE_DBG("SDK version:%s\n", system_get_sdk_version()); + // system_print_meminfo(); + // os_printf("Heap size::%d.\n",system_get_free_heap_size()); + // os_delay_us(50*1000); // delay 50ms before init uart + +#ifdef DEVELOP_VERSION + uart_init(BIT_RATE_74880, BIT_RATE_74880); +#else + uart_init(BIT_RATE_9600, BIT_RATE_9600); +#endif + // uart_init(BIT_RATE_115200, BIT_RATE_115200); + + #ifndef NODE_DEBUG + system_set_os_print(0); + #endif + + NODE_ERR("\n"); + // Initialize platform first for lua modules. + if( platform_init() != PLATFORM_OK ) + { + // This should never happen + NODE_DBG("Can not init platform for modules.\n"); + return; + } +#if defined( BUILD_WOFS ) + romfs_init(); + + // if( !wofs_format() ) + // { + // NODE_ERR( "\ni*** ERROR ***: unable to erase the flash. WOFS might be compromised.\n" ); + // NODE_ERR( "It is advised to re-flash the NodeWifi image.\n" ); + // } + // else + // NODE_ERR( "format done.\n" ); + + // test_romfs(); +#elif defined ( BUILD_SPIFFS ) + spiffs_mount(); + // test_spiffs(); +#endif + // endpoint_setup(); + + // char* lua_argv[] = { (char *)"lua", (char *)"-e", (char *)"print(collectgarbage'count');ttt={};for i=1,100 do table.insert(ttt,i*2 -1);print(i);end for k, v in pairs(ttt) do print('<'..k..' '..v..'>') end print(collectgarbage'count');", NULL }; + // lua_main( 3, lua_argv ); + // char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL }; + // lua_main( 2, lua_argv ); + // char* lua_argv[] = { (char *)"lua", (char *)"-e", (char *)"pwm.setup(0,100,50) pwm.start(0) pwm.stop(0)", NULL }; + // lua_main( 3, lua_argv ); + + task_init(); + system_os_post(USER_TASK_PRIO_0,SIG_LUA,'s'); +} diff --git a/app/wofs/Makefile b/app/wofs/Makefile new file mode 100644 index 00000000..00070c13 --- /dev/null +++ b/app/wofs/Makefile @@ -0,0 +1,44 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +GEN_LIBS = wofs.a +endif + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ../libc +INCLUDES += -I ../platform +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile diff --git a/app/wofs/romfiles.h b/app/wofs/romfiles.h new file mode 100644 index 00000000..40a41c32 --- /dev/null +++ b/app/wofs/romfiles.h @@ -0,0 +1,12 @@ +// Generated by mkfs.lua +// DO NOT MODIFY + +#ifndef __ROMFILES_H__ +#define __ROMFILES_H__ + +const unsigned char romfiles_fs[] = +{ + 0xFF +}; + +#endif diff --git a/app/wofs/romfs.c b/app/wofs/romfs.c new file mode 100644 index 00000000..29d0b8d4 --- /dev/null +++ b/app/wofs/romfs.c @@ -0,0 +1,565 @@ +// Filesystem implementation +#include "romfs.h" +#include "c_string.h" +// #include "c_errno.h" +#include "romfiles.h" +#include "c_stdio.h" +// #include "c_stdlib.h" +#include "c_fcntl.h" + +#include "platform.h" + +#if defined( BUILD_ROMFS ) || defined( BUILD_WOFS ) + +#define TOTAL_MAX_FDS 8 +// DO NOT CHANGE THE ROMFS ALIGNMENT. +// UNLESS YOU _LIKE_ TO WATCH THE WORLD BURN. +#define ROMFS_ALIGN 4 + +#define fsmin( x , y ) ( ( x ) < ( y ) ? ( x ) : ( y ) ) + +static FD fd_table[ TOTAL_MAX_FDS ]; +static int romfs_num_fd; + +#define WOFS_END_MARKER_CHAR 0xFF +#define WOFS_DEL_FIELD_SIZE ( ROMFS_ALIGN ) +#define WOFS_FILE_DELETED 0xAA + +// Length of the 'file size' field for both ROMFS/WOFS +#define ROMFS_SIZE_LEN 4 + +static int ICACHE_FLASH_ATTR romfs_find_empty_fd(void) +{ + int i; + + for( i = 0; i < TOTAL_MAX_FDS; i ++ ) + if( fd_table[ i ].baseaddr == 0xFFFFFFFF && + fd_table[ i ].offset == 0xFFFFFFFF && + fd_table[ i ].size == 0xFFFFFFFF ) + return i; + return -1; +} + +static void ICACHE_FLASH_ATTR romfs_close_fd( int fd ) +{ + if(fd<0 || fd>=TOTAL_MAX_FDS) + return; + c_memset( fd_table + fd, 0xFF, sizeof( FD ) ); + fd_table[ fd ].flags = 0; +} + +// Helper function: read a byte from the FS +static uint8_t ICACHE_FLASH_ATTR romfsh_read8( uint32_t addr, const FSDATA *pfs ) +{ + uint8_t temp; + if( pfs->flags & ROMFS_FS_FLAG_DIRECT ) + return pfs->pbase[ addr ]; + pfs->readf( &temp, addr, 1, pfs ); + return temp; +} + +// Helper function: return 1 if PFS reffers to a WOFS, 0 otherwise +static int ICACHE_FLASH_ATTR romfsh_is_wofs( const FSDATA* pfs ) +{ + return ( pfs->flags & ROMFS_FS_FLAG_WO ) != 0; +} + +// Find the next file, returning FS_FILE_OK or FS_FILE_NOT_FOUND if there no file left. +static uint8_t ICACHE_FLASH_ATTR romfs_next_file( uint32_t *start, char* fname, size_t len, size_t *act_len, FSDATA *pfs ) +{ + uint32_t i, j, n; + uint32_t fsize; + int is_deleted; + + // Look for the file + i = *start; + *act_len = 0; + if( (i >= INTERNAL_FLASH_SIZE) || (romfsh_read8( i, pfs ) == WOFS_END_MARKER_CHAR )) // end of file system + { + *start = (i >= INTERNAL_FLASH_SIZE)?(INTERNAL_FLASH_SIZE-1):i; + return FS_FILE_NOT_FOUND; + } + // Read file name + len = len>MAX_FNAME_LENGTH?MAX_FNAME_LENGTH:len; + for( j = 0; j < len; j ++ ) + { + fname[ j ] = romfsh_read8( i + j, pfs ); + if( fname[ j ] == 0 ) + break; + } + n = j; // save the file name length to n + // ' i + j' now points at the '0' byte + j = i + j + 1; + // Round to a multiple of ROMFS_ALIGN + j = ( j + ROMFS_ALIGN - 1 ) & ~( ROMFS_ALIGN - 1 ); + // WOFS has an additional WOFS_DEL_FIELD_SIZE bytes before the size as an indication for "file deleted" + if( romfsh_is_wofs( pfs ) ) + { + is_deleted = romfsh_read8( j, pfs ) == WOFS_FILE_DELETED; + j += WOFS_DEL_FIELD_SIZE; + } + else + is_deleted = 0; + // And read the size + fsize = romfsh_read8( j, pfs ) + ( romfsh_read8( j + 1, pfs ) << 8 ); + fsize += ( romfsh_read8( j + 2, pfs ) << 16 ) + ( romfsh_read8( j + 3, pfs ) << 24 ); + j += ROMFS_SIZE_LEN; + if( !is_deleted ) + { + // Found the valid file + *act_len = n; + } + // Move to next file + i = j + fsize; + // On WOFS, all file names must begin at a multiple of ROMFS_ALIGN + if( romfsh_is_wofs( pfs ) ) + i = ( i + ROMFS_ALIGN - 1 ) & ~( ROMFS_ALIGN - 1 ); + *start = i; // modify the start address + return FS_FILE_OK; +} + +// Open the given file, returning one of FS_FILE_NOT_FOUND, FS_FILE_ALREADY_OPENED +// or FS_FILE_OK +static uint8_t ICACHE_FLASH_ATTR romfs_open_file( const char* fname, FD* pfd, FSDATA *pfs, uint32_t *plast, uint32_t *pnameaddr ) +{ + uint32_t i, j, n; + char fsname[ MAX_FNAME_LENGTH + 1 ]; + uint32_t fsize; + int is_deleted; + + // Look for the file + i = 0; + while( 1 ) + { + if( i >= INTERNAL_FLASH_SIZE ){ + *plast = INTERNAL_FLASH_SIZE - 1; // point to last one + return FS_FILE_NOT_FOUND; + } + if( romfsh_read8( i, pfs ) == WOFS_END_MARKER_CHAR ) + { + *plast = i; + return FS_FILE_NOT_FOUND; + } + // Read file name + n = i; + for( j = 0; j < MAX_FNAME_LENGTH; j ++ ) + { + fsname[ j ] = romfsh_read8( i + j, pfs ); + if( fsname[ j ] == 0 ) + break; + } + // ' i + j' now points at the '0' byte + j = i + j + 1; + // Round to a multiple of ROMFS_ALIGN + j = ( j + ROMFS_ALIGN - 1 ) & ~( ROMFS_ALIGN - 1 ); + // WOFS has an additional WOFS_DEL_FIELD_SIZE bytes before the size as an indication for "file deleted" + if( romfsh_is_wofs( pfs ) ) + { + is_deleted = romfsh_read8( j, pfs ) == WOFS_FILE_DELETED; + j += WOFS_DEL_FIELD_SIZE; + } + else + is_deleted = 0; + // And read the size + fsize = romfsh_read8( j, pfs ) + ( romfsh_read8( j + 1, pfs ) << 8 ); + fsize += ( romfsh_read8( j + 2, pfs ) << 16 ) + ( romfsh_read8( j + 3, pfs ) << 24 ); + j += ROMFS_SIZE_LEN; + if( !c_strncasecmp( fname, fsname, MAX_FNAME_LENGTH ) && !is_deleted ) + { + // Found the file + pfd->baseaddr = j; + pfd->offset = 0; + pfd->size = fsize; + if( pnameaddr ) + *pnameaddr = n; + return FS_FILE_OK; + } + // Move to next file + i = j + fsize; + // On WOFS, all file names must begin at a multiple of ROMFS_ALIGN + if( romfsh_is_wofs( pfs ) ) + i = ( i + ROMFS_ALIGN - 1 ) & ~( ROMFS_ALIGN - 1 ); + } + *plast = 0; + return FS_FILE_NOT_FOUND; +} + +static int ICACHE_FLASH_ATTR romfs_open( const char *path, int flags, int mode, void *pdata ) +{ + FD tempfs; + int i; + FSDATA *pfsdata = ( FSDATA* )pdata; + int must_create = 0; + int exists; + uint8_t lflags = ROMFS_FILE_FLAG_READ; + uint32_t firstfree, nameaddr; + + if( romfs_num_fd == TOTAL_MAX_FDS ) + { + return -1; + } + // Does the file exist? + exists = romfs_open_file( path, &tempfs, pfsdata, &firstfree, &nameaddr ) == FS_FILE_OK; + // Now interpret "flags" to set file flags and to check if we should create the file + if( flags & O_CREAT ) + { + // If O_CREAT is specified with O_EXCL and the file already exists, return with error + if( ( flags & O_EXCL ) && exists ) + { + return -1; + } + // Otherwise create the file if it does not exist + must_create = !exists; + } + if( ( flags & O_TRUNC ) && ( flags & ( O_WRONLY | O_RDWR ) ) && exists ) + { + // The file exists, but it must be truncated + // In the case of WOFS, this effectively means "create a new file" + must_create = 1; + } + // ROMFS can't create files + if( must_create && ( ( pfsdata->flags & ROMFS_FS_FLAG_WO ) == 0 ) ) + { + return -1; + } + // Decode access mode + if( flags & O_WRONLY ) + lflags = ROMFS_FILE_FLAG_WRITE; + else if( flags & O_RDWR ) + lflags = ROMFS_FILE_FLAG_READ | ROMFS_FILE_FLAG_WRITE; + if( flags & O_APPEND ) + lflags |= ROMFS_FILE_FLAG_APPEND; + // If a write access is requested when the file must NOT be created, this + // is an error + if( ( lflags & ( ROMFS_FILE_FLAG_WRITE | ROMFS_FILE_FLAG_APPEND ) ) && !must_create ) + { + return -1; + } + if( ( lflags & ( ROMFS_FILE_FLAG_WRITE | ROMFS_FILE_FLAG_APPEND ) ) && romfs_fs_is_flag_set( pfsdata, ROMFS_FS_FLAG_WRITING ) ) + { + // At most one file can be opened in write mode at any given time on WOFS + return -1; + } + // Do we need to create the file ? + if( must_create ) + { + if( exists ) + { + // Invalidate the file first by changing WOFS_DEL_FIELD_SIZE bytes before + // the file length to WOFS_FILE_DELETED + uint8_t tempb[] = { WOFS_FILE_DELETED, 0xFF, 0xFF, 0xFF }; + pfsdata->writef( tempb, tempfs.baseaddr - ROMFS_SIZE_LEN - WOFS_DEL_FIELD_SIZE, WOFS_DEL_FIELD_SIZE, pfsdata ); + } + // Find the last available position by asking romfs_open_file to look for a file + // with an invalid name + romfs_open_file( "\1", &tempfs, pfsdata, &firstfree, NULL ); + // Is there enough space on the FS for another file? + if( pfsdata->max_size - firstfree + 1 < c_strlen( path ) + 1 + WOFS_MIN_NEEDED_SIZE + WOFS_DEL_FIELD_SIZE ) + { + return -1; + } + + // Make sure we can get a file descriptor before writing + if( ( i = romfs_find_empty_fd() ) < 0 ) + { + return -1; + } + + // Write the name of the file + pfsdata->writef( path, firstfree, c_strlen( path ) + 1, pfsdata ); + firstfree += c_strlen( path ) + 1; // skip over the name + // Align to a multiple of ROMFS_ALIGN + firstfree = ( firstfree + ROMFS_ALIGN - 1 ) & ~( ROMFS_ALIGN - 1 ); + firstfree += ROMFS_SIZE_LEN + WOFS_DEL_FIELD_SIZE; // skip over the size and the deleted flags area + tempfs.baseaddr = firstfree; + tempfs.offset = tempfs.size = 0; + // Set the "writing" flag on the FS to indicate that there is a file opened in write mode + romfs_fs_set_flag( pfsdata, ROMFS_FS_FLAG_WRITING ); + } + else // File must exist (and was found in the previous 'romfs_open_file' call) + { + if( !exists ) + { + return -1; + } + + if( ( i = romfs_find_empty_fd() ) < 0 ) + { + return -1; + } + } + // Copy the descriptor information + tempfs.flags = lflags; + c_memcpy( fd_table + i, &tempfs, sizeof( FD ) ); + romfs_num_fd ++; + return i; +} + +static int ICACHE_FLASH_ATTR romfs_close( int fd, void *pdata ) +{ + if(fd<0 || fd>=TOTAL_MAX_FDS) + return 0; + FD* pfd = fd_table + fd; + FSDATA *pfsdata = ( FSDATA* )pdata; + uint8_t temp[ ROMFS_SIZE_LEN ]; + + if( pfd->flags & ( ROMFS_FILE_FLAG_WRITE | ROMFS_FILE_FLAG_APPEND ) ) + { + // Write back the size + temp[ 0 ] = pfd->size & 0xFF; + temp[ 1 ] = ( pfd->size >> 8 ) & 0xFF; + temp[ 2 ] = ( pfd->size >> 16 ) & 0xFF; + temp[ 3 ] = ( pfd->size >> 24 ) & 0xFF; + pfsdata->writef( temp, pfd->baseaddr - ROMFS_SIZE_LEN, ROMFS_SIZE_LEN, pfsdata ); + // Clear the "writing" flag on the FS instance to allow other files to be opened + // in write mode + romfs_fs_clear_flag( pfsdata, ROMFS_FS_FLAG_WRITING ); + } + romfs_close_fd( fd ); + romfs_num_fd --; + return 0; +} + +static _ssize_t ICACHE_FLASH_ATTR romfs_write( int fd, const void* ptr, size_t len, void *pdata ) +{ + if(fd<0 || fd>=TOTAL_MAX_FDS) + return -1; + if(len == 0) + return 0; + FD* pfd = fd_table + fd; + FSDATA *pfsdata = ( FSDATA* )pdata; + + if( ( pfd->flags & ( ROMFS_FILE_FLAG_WRITE | ROMFS_FILE_FLAG_APPEND ) ) == 0 ) + { + return -1; + } + // Append mode: set the file pointer to the end + if( pfd->flags & ROMFS_FILE_FLAG_APPEND ) + pfd->offset = pfd->size; + // Only write at the end of the file! + if( pfd->offset != pfd->size ) + return 0; + // Check if we have enough space left on the device. Always keep 1 byte for the final 0xFF + // and ROMFS_ALIGN - 1 bytes for aligning the contents of the file data in the worst case + // scenario (so ROMFS_ALIGN bytes in total) + if( pfd->baseaddr + pfd->size + len > pfsdata->max_size - ROMFS_ALIGN ) + len = pfsdata->max_size - ( pfd->baseaddr + pfd->size ) - ROMFS_ALIGN; + pfsdata->writef( ptr, pfd->offset + pfd->baseaddr, len, pfsdata ); + pfd->offset += len; + pfd->size += len; + return len; +} + +static _ssize_t ICACHE_FLASH_ATTR romfs_read( int fd, void* ptr, size_t len, void *pdata ) +{ + if(fd<0 || fd>=TOTAL_MAX_FDS) + return -1; + if(len == 0) + return 0; + + FD* pfd = fd_table + fd; + long actlen = fsmin( len, pfd->size - pfd->offset ); + FSDATA *pfsdata = ( FSDATA* )pdata; + + if( ( pfd->flags & ROMFS_FILE_FLAG_READ ) == 0 ) + { + return -1; + } + if( pfsdata->flags & ROMFS_FS_FLAG_DIRECT ) + c_memcpy( ptr, pfsdata->pbase + pfd->offset + pfd->baseaddr, actlen ); + else + actlen = pfsdata->readf( ptr, pfd->offset + pfd->baseaddr, actlen, pfsdata ); + pfd->offset += actlen; + return actlen; +} + +// lseek +static int ICACHE_FLASH_ATTR romfs_lseek( int fd, int off, int whence, void *pdata ) +{ + if(fd<0 || fd>=TOTAL_MAX_FDS) + return -1; + + FD* pfd = fd_table + fd; + uint32_t newpos = 0; + + switch( whence ) + { + case SEEK_SET: + newpos = off; + break; + + case SEEK_CUR: + newpos = pfd->offset + off; + break; + + case SEEK_END: + newpos = pfd->size + off; + break; + + default: + return -1; + } + if( newpos > pfd->size ) + return -1; + pfd->offset = newpos; + return newpos; +} + +// **************************************************************************** +// WOFS functions and instance descriptor for real hardware + +#if defined( BUILD_WOFS ) +static uint32_t ICACHE_FLASH_ATTR sim_wofs_write( const void *from, uint32_t toaddr, uint32_t size, const void *pdata ) +{ + const FSDATA *pfsdata = ( const FSDATA* )pdata; + if(toaddr>=INTERNAL_FLASH_SIZE) + { + NODE_ERR("ERROR in flash op: wrong addr.\n"); + return 0; + } + toaddr += ( uint32_t )pfsdata->pbase; + return platform_flash_write( from, toaddr, size ); +} + +static uint32_t ICACHE_FLASH_ATTR sim_wofs_read( void *to, uint32_t fromaddr, uint32_t size, const void *pdata ) +{ + const FSDATA *pfsdata = ( const FSDATA* )pdata; + if(fromaddr>=INTERNAL_FLASH_SIZE) + { + NODE_ERR("ERROR in flash op: wrong addr.\n"); + return 0; + } + fromaddr += ( uint32_t )pfsdata->pbase; + return platform_flash_read( to, fromaddr, size ); +} + +// This must NOT be a const! +static FSDATA wofs_fsdata = +{ + NULL, + ROMFS_FS_FLAG_WO, + sim_wofs_read, + sim_wofs_write, + 0 +}; + +// WOFS formatting function +// Returns 1 if OK, 0 for error +int ICACHE_FLASH_ATTR wofs_format( void ) +{ + uint32_t sect_first, sect_last; + FD tempfd; + platform_flash_get_first_free_block_address( §_first ); + // Get the first free address in WOFS. We use this address to compute the last block that we need to + // erase, instead of simply erasing everything from sect_first to the last Flash page. + romfs_open_file( "\1", &tempfd, &wofs_fsdata, §_last, NULL ); + sect_last = platform_flash_get_sector_of_address( sect_last + ( uint32_t )wofs_fsdata.pbase ); + while( sect_first <= sect_last ) + if( platform_flash_erase_sector( sect_first ++ ) == PLATFORM_ERR ) + return 0; + return 1; +} + +int ICACHE_FLASH_ATTR wofs_open(const char *_name, int flags){ + return romfs_open( _name, flags, 0, &wofs_fsdata ); +} + +int ICACHE_FLASH_ATTR wofs_close( int fd ){ + return romfs_close( fd, &wofs_fsdata ); +} + +size_t ICACHE_FLASH_ATTR wofs_write( int fd, const void* ptr, size_t len ){ + return romfs_write( fd, ptr, len, &wofs_fsdata ); +} + +size_t ICACHE_FLASH_ATTR wofs_read( int fd, void* ptr, size_t len){ + return romfs_read( fd, ptr, len, &wofs_fsdata ); +} + +int ICACHE_FLASH_ATTR wofs_lseek( int fd, int off, int whence ){ + return romfs_lseek( fd, off, whence, &wofs_fsdata ); +} + +int ICACHE_FLASH_ATTR wofs_eof( int fd ){ + if(fd<0 || fd>=TOTAL_MAX_FDS) + return -1; + FD* pfd = fd_table + fd; + // NODE_DBG("off:%d, sz:%d\n",pfd->offset, pfd->size); + return pfd->offset == pfd->size; +} + +int ICACHE_FLASH_ATTR wofs_getc( int fd ){ + char c = EOF; + if(!wofs_eof(fd)){ + romfs_read( fd, &c, 1, &wofs_fsdata ); + } + // NODE_DBG("c: %d\n", c); + return (int)c; +} + +int ICACHE_FLASH_ATTR wofs_ungetc( int c, int fd ){ + return romfs_lseek( fd, -1, SEEK_CUR, &wofs_fsdata ); +} + +// Find the next file, returning FS_FILE_OK or FS_FILE_NOT_FOUND if there no file left. +uint8_t ICACHE_FLASH_ATTR wofs_next( uint32_t *start, char* fname, size_t len, size_t *act_len ){ + return romfs_next_file( start, fname, len, act_len, &wofs_fsdata ); +} + +#endif // #ifdef BUILD_WOFS + +// Initialize both ROMFS and WOFS as needed +int ICACHE_FLASH_ATTR romfs_init( void ) +{ + unsigned i; + + for( i = 0; i < TOTAL_MAX_FDS; i ++ ) + { + c_memset( fd_table + i, 0xFF, sizeof( FD ) ); + fd_table[ i ].flags = 0; + } +#if defined( BUILD_WOFS ) + // Get the start address and size of WOFS and register it + wofs_fsdata.pbase = ( uint8_t* )platform_flash_get_first_free_block_address( NULL ); + wofs_fsdata.max_size = INTERNAL_FLASH_SIZE - ( ( uint32_t )wofs_fsdata.pbase - INTERNAL_FLASH_START_ADDRESS ); + NODE_DBG("wofs.pbase:%x,max:%x\n",wofs_fsdata.pbase,wofs_fsdata.max_size); +#endif // ifdef BUILD_WOFS + return 0; +} + +#else // #if defined( BUILD_ROMFS ) || defined( BUILD_WOFS ) + +int ICACHE_FLASH_ATTR romfs_init( void ) +{ +} + +#endif // #if defined( BUILD_ROMFS ) || defined( BUILD_WOFS ) + +int ICACHE_FLASH_ATTR test_romfs() +{ + int fd; + int i, size; + + fd = wofs_open("init.lua",O_RDONLY); + NODE_DBG("open file fd:%d\n", fd); + char r[128]; + NODE_DBG("read from file:\n"); + c_memset(r,0,128); + size = wofs_read(fd,r,128); + r[size]=0; + NODE_DBG(r); + NODE_DBG("\n"); + wofs_close(fd); + + fd = wofs_open("testm.lua",O_RDONLY); + NODE_DBG("open file fd:%d\n", fd); + NODE_DBG("read from file:\n"); + c_memset(r,0,128); + size = wofs_read(fd,r,128); + r[size]=0; + NODE_DBG(r); + NODE_DBG("\n"); + wofs_close(fd); + + return 0; +} diff --git a/app/wofs/romfs.h b/app/wofs/romfs.h new file mode 100644 index 00000000..fe995c61 --- /dev/null +++ b/app/wofs/romfs.h @@ -0,0 +1,101 @@ +// Read-only ROM filesystem + +#ifndef __ROMFS_H__ +#define __ROMFS_H__ + +#include "c_types.h" +#include "c_fcntl.h" + +/******************************************************************************* +The Read-Only "filesystem" resides in a contiguous zone of memory, with the +following structure (repeated for each file): + +Filename: ASCIIZ, max length is DM_MAX_FNAME_LENGTH, first byte is 0xFF if last file +File size: (4 bytes), aligned to ROMFS_ALIGN bytes +File data: (file size bytes) + +The WOFS (Write Once File System) uses much of the ROMFS functions, thuss it is +also implemented in romfs.c. It resides in a contiguous zone of memory, with a +structure that is quite similar with ROMFS' structure (repeated for each file): + +Filename: ASCIIZ, max length is DM_MAX_FNAME_LENGTH, first byte is 0xFF if last file. + WOFS filenames always begin at an address which is a multiple of ROMFS_ALIGN. +File deleted flag: (WOFS_DEL_FIELD_SIZE bytes), aligned to ROMFS_ALIGN bytes +File size: (4 bytes), aligned to ROMFS_ALIGN bytes +File data: (file size bytes) + +*******************************************************************************/ + +// GLOBAL maximum file length (on ALL supported filesystem) +#define MAX_FNAME_LENGTH 30 + +enum +{ + FS_FILE_NOT_FOUND, + FS_FILE_OK +}; + +// ROMFS/WOFS functions +typedef uint32_t ( *p_fs_read )( void *to, uint32_t fromaddr, uint32_t size, const void *pdata ); +typedef uint32_t ( *p_fs_write )( const void *from, uint32_t toaddr, uint32_t size, const void *pdata ); + +// File flags +#define ROMFS_FILE_FLAG_READ 0x01 +#define ROMFS_FILE_FLAG_WRITE 0x02 +#define ROMFS_FILE_FLAG_APPEND 0x04 + +// A small "FILE" structure +typedef struct +{ + uint32_t baseaddr; + uint32_t offset; + uint32_t size; + uint8_t flags; +} FD; + +// WOFS constants +// The miminum size we need in order to create another file +// This size will be added to the size of the filename when creating a new file +// to ensure that there's enough space left on the device +// This comes from the size of the file length field (4) + the maximum number of +// bytes needed to align this field (3) + a single 0xFF byte which marks the end +// of the filesystem (1) + the maximum number of bytes needed to align the contents +// of a file (3) +#define WOFS_MIN_NEEDED_SIZE 11 + +// Filesystem flags +#define ROMFS_FS_FLAG_DIRECT 0x01 // direct mode (the file is mapped in a memory area directly accesible by the CPU) +#define ROMFS_FS_FLAG_WO 0x02 // this FS is actually a WO (Write-Once) FS +#define ROMFS_FS_FLAG_WRITING 0x04 // for WO only: there is already a file opened in write mode + +// File system descriptor +typedef struct +{ + uint8_t *pbase; // pointer to FS base in memory (only for ROMFS_FS_FLAG_DIRECT) + uint8_t flags; // flags (see above) + p_fs_read readf; // pointer to read function (for non-direct mode FS) + p_fs_write writef; // pointer to write function (only for ROMFS_FS_FLAG_WO) + uint32_t max_size; // maximum size of the FS (in bytes) +} FSDATA; + +#define romfs_fs_set_flag( p, f ) p->flags |= ( f ) +#define romfs_fs_clear_flag( p, f ) p->flags &= ( uint8_t )~( f ) +#define romfs_fs_is_flag_set( p, f ) ( ( p->flags & ( f ) ) != 0 ) + +#if defined( BUILD_WOFS ) +int wofs_format( void ); +int wofs_open(const char *name, int flags); +int wofs_close( int fd ); +size_t wofs_write( int fd, const void* ptr, size_t len ); +size_t wofs_read( int fd, void* ptr, size_t len); +int wofs_lseek( int fd, int off, int whence ); +int wofs_eof( int fd ); +int wofs_getc( int fd ); +int wofs_ungetc( int c, int fd ); +uint8_t wofs_next( uint32_t *start, char* fname, size_t len, size_t *act_len ); // for list file name +#endif +// FS functions +int romfs_init( void ); + +#endif + diff --git a/bin/.gitignore b/bin/.gitignore new file mode 100644 index 00000000..081b0eda --- /dev/null +++ b/bin/.gitignore @@ -0,0 +1,7 @@ +* +*.S +*.dump +*.bin +*.bin_rep +!.gitignore + diff --git a/examples/fragment.lua b/examples/fragment.lua new file mode 100644 index 00000000..79256373 --- /dev/null +++ b/examples/fragment.lua @@ -0,0 +1,303 @@ +pwm.setup(0,500,50) pwm.setup(1,500,50) pwm.setup(2,500,50) +pwm.start(0) pwm.start(1) pwm.start(2) +function led(r,g,b) pwm.setduty(0,g) pwm.setduty(1,b) pwm.setduty(2,r) end +wifi.station.autoconnect(1) +a=0 +tmr.alarm( 1000,1,function() if a==0 then a=1 led(50,50,50) else a=0 led(0,0,0) end end) + +sv:on("receive", function(s,c) s:send("

Hello, world.

") print(c) end ) + + +sk=net.createConnection(net.TCP, 0) +sk:on("receive", function(sck, c) print(c) end ) +sk:connect(80,"115.239.210.27") +sk:send("GET / HTTP/1.1\r\nHost: 115.239.210.27\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") + +sk:connect(80,"192.168.0.66") +sk:send("GET / HTTP/1.1\r\nHost: 1192.168.0.66\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") + +i2c.setup(0,1,0,i2c.SLOW) +function read_bmp(addr) i2c.start(0) i2c.address(0,119,i2c.RECEIVER) c=i2c.read(0,1) i2c.stop(0) print(string.byte(c)) end +function read_bmp(addr) i2c.start(0) i2c.address(0,119,i2c.TRANSMITTER) i2c.write(0,addr) i2c.stop(0) i2c.start(0) i2c.address(0,119,i2c.RECEIVER) c=i2c.read(0,2) i2c.stop(0) return c end + +s=net.createServer(net.TCP) s:listen(80,function(c) end) +ss=net.createServer(net.TCP) ss:listen(80,function(c) end) + +s=net.createServer(net.TCP) s:listen(80,function(c) c:on("receive",function(s,c) print(c) end) end) + +s=net.createServer(net.UDP) s:listen(5683,function(c) c:on("receive",function(s,c) print(c) end) end) + +mm=node.list() +for k, v in pairs(mm) do print('file:'..k..' len:'..v) end +for k,v in pairs(d) do print("n:"..k..", s:"..v) end +su=net.createConnection(net.UDP) +su:on("receive",function(su,c) print(c) end) +su:connect(5683,"192.168.0.66") +su:send("/v1/id") + +gpio.mode(0,gpio.INT) gpio.trig(0,"down",function(l) print("level="..l) end) + +t0 = 0; +function tr0(l) print(tmr.now() - t0) t0 = tmr.now() + if l==1 then gpio.trig(0,"down") else gpio.trig(0,"up") end end +gpio.mode(0,gpio.INT) +gpio.trig(0,"down",tr0) + + +su=net.createConnection(net.UDP) +su:on("receive",function(su,c) print(c) end) +su:connect(5001,"114.215.154.114") +su:send([[{"type":"signin","name":"nodemcu","password":"123456"}]]) + +su:send([[{"type":"signout","name":"nodemcu","password":"123456"}]]) + +su:send([[{"type":"connect","from":"nodemcu","to":"JYP","password":"123456"}]]) + +su:send("hello world") + +s=net.createServer(net.TCP) s:listen(8008,function(c) c:on("receive",function(s,c) print(c) pcall(loadstring(c)) end) end) + +s=net.createServer(net.UDP) s:listen(8888,function(c) c:on("receive",function(s,c) print(c) pcall(loadstring(c)) end) end) + +s=net.createServer(net.TCP) s:listen(8008,function(c) con_std = c function s_output(str) if(con_std~=nil) then con_std:send(str) end end + node.output(s_output, 0) c:on("receive",function(c,l) node.input(l) end) c:on("disconnection",function(c) con_std = nil node.output(nil) end) end) + +s=net.createServer(net.TCP) +s:listen(23,function(c) + con_std = c + function s_output(str) + if(con_std~=nil) + then con_std:send(str) + end + end + node.output(s_output, 0) + c:on("receive",function(c,l) node.input(l) end) + c:on("disconnection",function(c) + con_std = nil + node.output(nil) + end) +end) + +srv=net.createServer(net.TCP) srv:listen(80,function(conn) conn:on("receive",function(conn,payload) +print(node.heap()) door="open" if gpio.read(8)==1 then door="open" else door="closed" end +conn:send("

Door Sensor. The door is " .. door ..".

") conn:close() end) end) + +srv=net.createServer(net.TCP) srv:listen(80,function(conn) conn:on("receive",function(conn,payload) +print(node.heap()) print(adc.read(0)) door="open" if gpio.read(0)==1 then door="open" else door="closed" end +conn:send("

Door Sensor. The door is " .. door ..".

") end) conn:on("sent",function(conn) conn:close() end) end) + +srv=net.createServer(net.TCP) srv:listen(80,function(conn) + conn:on("receive",function(conn,payload) + print(node.heap()) + door="open" + if gpio.read(0)==1 then door="open" else door="closed" end + conn:send("

Door Sensor. The door is " .. door ..".

") + end) + conn:on("sent",function(conn) conn:close() end) +end) + +port = 9999 +hostip = "192.168.1.99" +sk=net.createConnection(net.TCP, false) +sk:on("receive", function(conn, pl) print(pl) end ) +sk:connect(port, hostip) + + +file.remove("init.lua") +file.open("init.lua","w") +file.writeline([[print("Petes Tester 4")]]) +file.writeline([[tmr.alarm(5000, 0, function() dofile("thelot.lua") end )]]) +file.close() + +file.remove("thelot.lua") +file.open("thelot.lua","w") +file.writeline([[tmr.stop()]]) +file.writeline([[connecttoap = function (ssid,pw)]]) +file.writeline([[print(wifi.sta.getip())]]) +file.writeline([[wifi.setmode(wifi.STATION)]]) +file.writeline([[tmr.delay(1000000)]]) +file.writeline([[wifi.sta.config(ssid,pw)]]) +file.writeline([[tmr.delay(5000000)]]) +file.writeline([[print("Connected to ",ssid," as ",wifi.sta.getip())]]) +file.writeline([[end]]) +file.writeline([[connecttoap("MyHub","0011223344")]]) +file.close() + + +s=net.createServer(net.UDP) s:listen(5683) s:on("receive",function(s,c) print(c) s:send("echo:"..c) end) +s:on("sent",function(s) print("echo donn") end) + +sk=net.createConnection(net.UDP, 0) sk:on("receive", function(sck, c) print(c) end ) sk:connect(8080,"192.168.0.88") +sk:send("GET / HTTP/1.1\r\nHost: 192.168.0.88\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") + +srv=net.createServer(net.TCP, 5) srv:listen(80,function(conn) conn:on("receive",function(conn,payload) +print(node.heap()) print(adc.read(0)) door="open" if gpio.read(0)==1 then door="open" else door="closed" end +conn:send("

Door Sensor. The door is " .. door ..".

") end) end) + +srv=net.createServer(net.TCP) +srv:listen(80,function(conn) +conn:on("receive",function(conn,payload) + print(payload) print(node.heap()) + conn:send("

Hello, NodeMcu.

") + end) +conn:on("sent",function(conn) conn:close() end) +end) + + +function startServer() +print("WIFI AP connected. Wicon IP:") +print(wifi.sta.getip()) +sv=net.createServer(net.TCP,180) +sv:listen(8080,function(conn) +print("Wifi console connected.") + +function s_output(str) +if(conn~=nil) then +conn:send(str) +end +end +node.output(s_output,0) + +conn:on("receive",function(conn,pl) +node.input(pl) +if (conn==nil) then +print("conn is nil") +end + +print("hello") + +mycounter=0 srv=net.createServer(net.TCP) srv:listen(80,function(conn) conn:on("receive",function(conn,payload) +if string.find(payload,"?myarg=") then mycounter=mycounter+1 +m="
Value= " .. string.sub(payload,string.find(payload,"?myarg=")+7,string.find(payload,"HTTP")-2) else m="" end +conn:send("

Hello, this is Pete's web page.

How are you today.
Count=" .. mycounter .. m .. "Heap=".. node.heap()) +end) conn:on("sent",function(conn) conn:close() conn = nil end) end) + +srv=net.createServer(net.TCP) srv:listen(80,function(conn) conn:on("receive",function(conn,payload) + conn:send("HTTP/1.1 200 OK\r\n") conn:send("Connection: close\r\n\r\n") conn:send("

Hello, NodeMcu.

") + print(node.heap()) conn:close() end) end) + + +conn=net.createConnection(net.TCP) +conn:dns("www.nodemcu.com",function(conn,ip) print(ip) print("hell") end) + +function connected(conn) conn:on("receive",function(conn,payload) + conn:send("HTTP/1.1 200 OK\r\n") conn:send("Connection: close\r\n\r\n") conn:send("

Hello, NodeMcu.

") + print(node.heap()) conn:close() end) end +srv=net.createServer(net.TCP) +srv:on("connection",function(conn) conn:on("receive",function(conn,payload) + conn:send("HTTP/1.1 200 OK\r\n") conn:send("Connection: close\r\n\r\n") conn:send("

Hello, NodeMcu.

") + print(node.heap()) conn:close() end) end) +srv:listen(80) + + +-- sieve.lua +-- the sieve of Eratosthenes programmed with coroutines +-- typical usage: lua -e N=500 sieve.lua | column + +-- generate all the numbers from 2 to n +function gen (n) return coroutine.wrap(function () for i=2,n do coroutine.yield(i) end end) end + +-- filter the numbers generated by `g', removing multiples of `p' +function filter (p, g) return coroutine.wrap(function () for n in g do if n%p ~= 0 then coroutine.yield(n) end end end) end + +N=N or 500 -- from command line +x = gen(N) -- generate primes up to N +while 1 do + local n = x() -- pick a number until done + if n == nil then break end + print(n) -- must be a prime number + x = filter(n, x) -- now remove its multiples +end + +file.remove("mylistener.lua") +file.open("mylistener.lua","w") +file.writeline([[gpio2 = 9]]) +file.writeline([[gpio0 = 8]]) +file.writeline([[gpio.mode(gpio2,gpio.OUTPUT)]]) +file.writeline([[gpio.write(gpio2,gpio.LOW)]]) +file.writeline([[gpio.mode(gpio0,gpio.OUTPUT)]]) +file.writeline([[gpio.write(gpio0,gpio.LOW)]]) +file.writeline([[l1="0\n"]]) +file.writeline([[l2="0\n"]]) +file.writeline([[l3="0\n"]]) +file.writeline([[l4="0\n"]]) +file.writeline([[sv=net.createServer(net.TCP, 5) ]]) +file.writeline([[sv:listen(4000,function(c)]]) +file.writeline([[c:on("disconnection", function(c) print("Bye") end )]]) +file.writeline([[c:on("receive", function(sck, pl) ]]) +-- file.writeline([[print(pl) ]]) +file.writeline([[if (pl=="GO1\n") then c:send(l1) ]]) +file.writeline([[elseif pl=="GO2\n" then c:send(l2) ]]) +file.writeline([[elseif pl=="GO3\n" then c:send(l3) ]]) +file.writeline([[elseif pl=="GO4\n" then c:send(l4) ]]) +file.writeline([[elseif pl=="YES1\n" then l1="1\n" c:send("OK\n") gpio.write(gpio2,gpio.HIGH) ]]) +file.writeline([[elseif pl=="NO1\n" then l1="0\n" c:send("OK\n") gpio.write(gpio2,gpio.LOW) ]]) +file.writeline([[elseif pl=="YES2\n" then l2="1\n" c:send("OK\n") gpio.write(gpio0,gpio.HIGH) ]]) +file.writeline([[elseif pl=="NO2\n" then l2="0\n" c:send("OK\n") gpio.write(gpio0,gpio.LOW) ]]) +file.writeline([[elseif pl=="YES3\n" then l3="1\n" c:send("OK\n") print(node.heap()) ]]) +file.writeline([[elseif pl=="NO3\n" then l3="0\n" c:send("OK\n") print(node.heap()) ]]) +file.writeline([[elseif pl=="YES4\n" then l4="1\n" c:send("OK\n") print(node.heap()) ]]) +file.writeline([[elseif pl=="NO4\n" then l4="0\n" c:send("OK\n") print(node.heap()) ]]) +file.writeline([[else c:send("0\n") print(node.heap()) ]]) +file.writeline([[end]]) +file.writeline([[end)]]) +file.writeline([[end)]]) +file.close() + +file.remove("myli.lua") file.open("myli.lua","w") +file.writeline([[sv=net.createServer(net.TCP, 5) ]]) +file.writeline([[sv:listen(4000,function(c)]]) +file.writeline([[c:on("disconnection", function(c) print("Bye") end )]]) +--file.writeline([[c:on("sent", function(c) c:close() end )]]) +file.writeline([[c:on("receive", function(sck, pl) ]]) +file.writeline([[sck:send("0\n") print(node.heap()) ]]) +file.writeline([[end)]]) file.writeline([[end)]]) file.close() + +sv=net.createServer(net.TCP, 50) sv:listen(4000,function(c) c:on("disconnection",function(c) print("Bye") end) + c:on("receive", function(sck, pl) sck:send("0\n") print(node.heap()) end) end) + +sv=net.createServer(net.TCP, 5) sv:listen(4000,function(c) c:on("disconnection",function(c) print("Bye") end) + c:on("receive", function(sck, pl) sck:send("0\n") print(node.heap()) end) c:on("sent", function(sck) sck:close() end) end) + +s=net.createServer(net.UDP) +s:on("receive",function(s,c) print(c) end) +s:listen(8888) + +print("This is a long long long line to test the memory limit of nodemcu firmware\n") +collectgarbage("setmemlimit",8) +print(collectgarbage("getmemlimit")) + +tmr.alarm(1,5000,1,function() print("alarm 1") end) +tmr.stop(1) + +tmr.alarm(0,1000,1,function() print("alarm 0") end) +tmr.stop(0) + +tmr.alarm(2,2000,1,function() print("alarm 2") end) +tmr.stop(2) + +tmr.alarm(6,2000,1,function() print("alarm 6") end) +tmr.stop(6) + +for k,v in pairs(_G.package.loaded) do print(k) end +for k,v in pairs(d) do print("n:"..k..", s:"..v) end + +a="pin=9" +t={} +for k, v in string.gmatch(a, "(%w+)=(%w+)") do t[k]=v end +print(t["pin"]) + +function switch() gpio.mode(4,gpio.OUTPUT) gpio.mode(5,gpio.OUTPUT) tmr.delay(1000000) print("hello world") end +tmr.alarm(0,10000,0,function () uart.setup(0,9600,8,0,1) end) switch() + +sk=net.createConnection(net.TCP, 0) sk:on("receive", function(sck, c) print(c) end ) sk:connect(80,"www.nodemcu.com") sk:send("GET / HTTP/1.1\r\nHost: www.nodemcu.com\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") + +sk=net.createConnection(net.TCP, 0) sk:on("receive", function(sck, c) print(c) end ) +sk:on("connection", function(sck) sck:send("GET / HTTP/1.1\r\nHost: www.nodemcu.com\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") end ) sk:connect(80,"www.nodemcu.com") + + +sk=net.createConnection(net.TCP, 0) sk:on("receive", function(sck, c) print(c) end ) sk:connect(80,"115.239.210.27") +sk:send("GET / HTTP/1.1\r\nHost: 115.239.210.27\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") + +sk=net.createConnection(net.TCP, 1) sk:on("receive", function(sck, c) print(c) end ) +sk:on("connection", function(sck) sck:send("GET / HTTPS/1.1\r\nHost: www.google.com.hk\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") end ) sk:connect(443,"173.194.72.199") \ No newline at end of file diff --git a/examples/telnet2.lua b/examples/tel.lua similarity index 100% rename from examples/telnet2.lua rename to examples/tel.lua diff --git a/examples/telnet.lua b/examples/telnet.lua index 18645187..482fcb41 100644 --- a/examples/telnet.lua +++ b/examples/telnet.lua @@ -3,30 +3,32 @@ print("Author: openthings@163.com. copyright&GPL V2.") print("Last modified 2014-11-19. V0.2") print("Wicon Server starting ......") -function connected(conn) - print("Wifi console connected.") - function s_output(str) - if (conn~=nil) then - conn:send(str) - end - end - node.output(s_output,0) - conn:on("receive", function(conn, pl) - node.input(pl) - end) - conn:on("disconnection",function(conn) - node.output(nil) - end) - print("Welcome to NodeMcu world.") -end - function startServer() print("Wifi AP connected. Wicon IP:") print(wifi.sta.getip()) sv=net.createServer(net.TCP, 180) - sv:listen(2323, connected) - print("Telnet Server running at :2323") - print("===Now, logon and input LUA.====") + sv:listen(8080, function(conn) + print("Wifi console connected.") + + function s_output(str) + if (conn~=nil) then + conn:send(str) + end + end + node.output(s_output,0) + + conn:on("receive", function(conn, pl) + node.input(pl) + if (conn==nil) then + print("conn is nil.") + end + end) + conn:on("disconnection",function(conn) + node.output(nil) + end) + end) + print("Wicon Server running at :8080") + print("===Now,Using xcon_tcp logon and input LUA.====") end tmr.alarm(1000, 1, function() diff --git a/include/c_types.h b/include/c_types.h new file mode 100644 index 00000000..88822345 --- /dev/null +++ b/include/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; +#if !defined(__size_t) + #define __size_t 1 + typedef unsigned int size_t; /* others (e.g. ) also define */ + /* the unsigned integral type of the result of the sizeof operator. */ +#endif + +#define __packed __attribute__((packed)) + +#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 __attribute__((section(".bss"))) +#define SHMEM_ATTR + +#ifdef ICACHE_FLASH +#define ICACHE_FLASH_ATTR __attribute__((section(".irom0.text"))) +#else +#define ICACHE_FLASH_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/include/eagle_soc.h b/include/eagle_soc.h new file mode 100644 index 00000000..9c4b8a01 --- /dev/null +++ b/include/eagle_soc.h @@ -0,0 +1,256 @@ +/* + * Copyright (c) Espressif System 2010 - 2012 + * + */ + +#ifndef _EAGLE_SOC_H_ +#define _EAGLE_SOC_H_ + +//Register Bits{{ +#define BIT31 0x80000000 +#define BIT30 0x40000000 +#define BIT29 0x20000000 +#define BIT28 0x10000000 +#define BIT27 0x08000000 +#define BIT26 0x04000000 +#define BIT25 0x02000000 +#define BIT24 0x01000000 +#define BIT23 0x00800000 +#define BIT22 0x00400000 +#define BIT21 0x00200000 +#define BIT20 0x00100000 +#define BIT19 0x00080000 +#define BIT18 0x00040000 +#define BIT17 0x00020000 +#define BIT16 0x00010000 +#define BIT15 0x00008000 +#define BIT14 0x00004000 +#define BIT13 0x00002000 +#define BIT12 0x00001000 +#define BIT11 0x00000800 +#define BIT10 0x00000400 +#define BIT9 0x00000200 +#define BIT8 0x00000100 +#define BIT7 0x00000080 +#define BIT6 0x00000040 +#define BIT5 0x00000020 +#define BIT4 0x00000010 +#define BIT3 0x00000008 +#define BIT2 0x00000004 +#define BIT1 0x00000002 +#define BIT0 0x00000001 +//}} + +//Registers Operation {{ +#define ETS_UNCACHED_ADDR(addr) (addr) +#define ETS_CACHED_ADDR(addr) (addr) + + +#define READ_PERI_REG(addr) (*((volatile uint32_t *)ETS_UNCACHED_ADDR(addr))) +#define WRITE_PERI_REG(addr, val) (*((volatile uint32_t *)ETS_UNCACHED_ADDR(addr))) = (uint32_t)(val) +#define CLEAR_PERI_REG_MASK(reg, mask) WRITE_PERI_REG((reg), (READ_PERI_REG(reg)&(~(mask)))) +#define SET_PERI_REG_MASK(reg, mask) WRITE_PERI_REG((reg), (READ_PERI_REG(reg)|(mask))) +#define GET_PERI_REG_BITS(reg, hipos,lowpos) ((READ_PERI_REG(reg)>>(lowpos))&((1<<((hipos)-(lowpos)+1))-1)) +#define SET_PERI_REG_BITS(reg,bit_map,value,shift) (WRITE_PERI_REG((reg),(READ_PERI_REG(reg)&(~((bit_map)<<(shift))))|((value)<<(shift)) )) +//}} + +//Periheral Clock {{ +#define CPU_CLK_FREQ 80*1000000 //unit: Hz +#define APB_CLK_FREQ CPU_CLK_FREQ +#define UART_CLK_FREQ APB_CLK_FREQ +#define TIMER_CLK_FREQ (APB_CLK_FREQ>>8) //divided by 256 +//}} + +//Peripheral device base address define{{ +#define PERIPHS_DPORT_BASEADDR 0x3ff00000 +#define PERIPHS_GPIO_BASEADDR 0x60000300 +#define PERIPHS_TIMER_BASEDDR 0x60000600 +#define PERIPHS_RTC_BASEADDR 0x60000700 +#define PERIPHS_IO_MUX 0x60000800 +//}} + +//Interrupt remap control registers define{{ +#define EDGE_INT_ENABLE_REG (PERIPHS_DPORT_BASEADDR+0x04) +#define TM1_EDGE_INT_ENABLE() SET_PERI_REG_MASK(EDGE_INT_ENABLE_REG, BIT1) +#define TM1_EDGE_INT_DISABLE() CLEAR_PERI_REG_MASK(EDGE_INT_ENABLE_REG, BIT1) +//}} + +//GPIO reg {{ +#define GPIO_REG_READ(reg) READ_PERI_REG(PERIPHS_GPIO_BASEADDR + reg) +#define GPIO_REG_WRITE(reg, val) WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + reg, val) +#define GPIO_OUT_ADDRESS 0x00 +#define GPIO_OUT_W1TS_ADDRESS 0x04 +#define GPIO_OUT_W1TC_ADDRESS 0x08 + +#define GPIO_ENABLE_ADDRESS 0x0c +#define GPIO_ENABLE_W1TS_ADDRESS 0x10 +#define GPIO_ENABLE_W1TC_ADDRESS 0x14 +#define GPIO_OUT_W1TC_DATA_MASK 0x0000ffff + +#define GPIO_IN_ADDRESS 0x18 + +#define GPIO_STATUS_ADDRESS 0x1c +#define GPIO_STATUS_W1TS_ADDRESS 0x20 +#define GPIO_STATUS_W1TC_ADDRESS 0x24 +#define GPIO_STATUS_INTERRUPT_MASK 0x0000ffff + +#define GPIO_RTC_CALIB_SYNC PERIPHS_GPIO_BASEADDR+0x6c +#define RTC_CALIB_START BIT31 //first write to zero, then to one to start +#define RTC_PERIOD_NUM_MASK 0x3ff //max 8ms +#define GPIO_RTC_CALIB_VALUE PERIPHS_GPIO_BASEADDR+0x70 +#define RTC_CALIB_RDY_S 31 //after measure, flag to one, when start from zero to one, turn to zero +#define RTC_CALIB_VALUE_MASK 0xfffff + +#define GPIO_PIN0_ADDRESS 0x28 + +#define GPIO_ID_PIN0 0 +#define GPIO_ID_PIN(n) (GPIO_ID_PIN0+(n)) +#define GPIO_LAST_REGISTER_ID GPIO_ID_PIN(15) +#define GPIO_ID_NONE 0xffffffff + +#define GPIO_PIN_COUNT 16 + +#define GPIO_PIN_CONFIG_MSB 12 +#define GPIO_PIN_CONFIG_LSB 11 +#define GPIO_PIN_CONFIG_MASK 0x00001800 +#define GPIO_PIN_CONFIG_GET(x) (((x) & GPIO_PIN_CONFIG_MASK) >> GPIO_PIN_CONFIG_LSB) +#define GPIO_PIN_CONFIG_SET(x) (((x) << GPIO_PIN_CONFIG_LSB) & GPIO_PIN_CONFIG_MASK) + +#define GPIO_WAKEUP_ENABLE 1 +#define GPIO_WAKEUP_DISABLE (~GPIO_WAKEUP_ENABLE) +#define GPIO_PIN_WAKEUP_ENABLE_MSB 10 +#define GPIO_PIN_WAKEUP_ENABLE_LSB 10 +#define GPIO_PIN_WAKEUP_ENABLE_MASK 0x00000400 +#define GPIO_PIN_WAKEUP_ENABLE_GET(x) (((x) & GPIO_PIN_WAKEUP_ENABLE_MASK) >> GPIO_PIN_WAKEUP_ENABLE_LSB) +#define GPIO_PIN_WAKEUP_ENABLE_SET(x) (((x) << GPIO_PIN_WAKEUP_ENABLE_LSB) & GPIO_PIN_WAKEUP_ENABLE_MASK) + +#define GPIO_PIN_INT_TYPE_MASK 0x380 +#define GPIO_PIN_INT_TYPE_MSB 9 +#define GPIO_PIN_INT_TYPE_LSB 7 +#define GPIO_PIN_INT_TYPE_GET(x) (((x) & GPIO_PIN_INT_TYPE_MASK) >> GPIO_PIN_INT_TYPE_LSB) +#define GPIO_PIN_INT_TYPE_SET(x) (((x) << GPIO_PIN_INT_TYPE_LSB) & GPIO_PIN_INT_TYPE_MASK) + +#define GPIO_PAD_DRIVER_ENABLE 1 +#define GPIO_PAD_DRIVER_DISABLE (~GPIO_PAD_DRIVER_ENABLE) +#define GPIO_PIN_PAD_DRIVER_MSB 2 +#define GPIO_PIN_PAD_DRIVER_LSB 2 +#define GPIO_PIN_PAD_DRIVER_MASK 0x00000004 +#define GPIO_PIN_PAD_DRIVER_GET(x) (((x) & GPIO_PIN_PAD_DRIVER_MASK) >> GPIO_PIN_PAD_DRIVER_LSB) +#define GPIO_PIN_PAD_DRIVER_SET(x) (((x) << GPIO_PIN_PAD_DRIVER_LSB) & GPIO_PIN_PAD_DRIVER_MASK) + +#define GPIO_AS_PIN_SOURCE 0 +#define SIGMA_AS_PIN_SOURCE (~GPIO_AS_PIN_SOURCE) +#define GPIO_PIN_SOURCE_MSB 0 +#define GPIO_PIN_SOURCE_LSB 0 +#define GPIO_PIN_SOURCE_MASK 0x00000001 +#define GPIO_PIN_SOURCE_GET(x) (((x) & GPIO_PIN_SOURCE_MASK) >> GPIO_PIN_SOURCE_LSB) +#define GPIO_PIN_SOURCE_SET(x) (((x) << GPIO_PIN_SOURCE_LSB) & GPIO_PIN_SOURCE_MASK) +// }} + +// TIMER reg {{ +#define RTC_REG_READ(addr) READ_PERI_REG(PERIPHS_TIMER_BASEDDR + addr) +#define RTC_REG_WRITE(addr, val) WRITE_PERI_REG(PERIPHS_TIMER_BASEDDR + addr, val) +#define RTC_CLR_REG_MASK(reg, mask) CLEAR_PERI_REG_MASK(PERIPHS_TIMER_BASEDDR +reg, mask) +/* Returns the current time according to the timer timer. */ +#define NOW() RTC_REG_READ(FRC2_COUNT_ADDRESS) + +//load initial_value to timer1 +#define FRC1_LOAD_ADDRESS 0x00 + +//timer1's counter value(count from initial_value to 0) +#define FRC1_COUNT_ADDRESS 0x04 + +#define FRC1_CTRL_ADDRESS 0x08 + +//clear timer1's interrupt when write this address +#define FRC1_INT_ADDRESS 0x0c +#define FRC1_INT_CLR_MASK 0x00000001 + +//timer2's counter value(count from initial_value to 0) +#define FRC2_COUNT_ADDRESS 0x24 +// }} + +//RTC reg {{ +#define REG_RTC_BASE PERIPHS_RTC_BASEADDR + +#define RTC_GPIO_OUT (REG_RTC_BASE + 0x068) +#define RTC_GPIO_ENABLE (REG_RTC_BASE + 0x074) +#define RTC_GPIO_IN_DATA (REG_RTC_BASE + 0x08C) +#define RTC_GPIO_CONF (REG_RTC_BASE + 0x090) +#define PAD_XPD_DCDC_CONF (REG_RTC_BASE + 0x0A0) +//}} + +//PIN Mux reg {{ +#define PERIPHS_IO_MUX_FUNC 0x13 +#define PERIPHS_IO_MUX_FUNC_S 4 +#define PERIPHS_IO_MUX_PULLUP BIT7 +#define PERIPHS_IO_MUX_PULLDWN BIT6 +#define PERIPHS_IO_MUX_SLEEP_PULLUP BIT3 +#define PERIPHS_IO_MUX_SLEEP_PULLDWN BIT2 +#define PERIPHS_IO_MUX_SLEEP_OE BIT1 +#define PERIPHS_IO_MUX_OE BIT0 + +#define PERIPHS_IO_MUX_CONF_U (PERIPHS_IO_MUX + 0x00) +#define SPI0_CLK_EQU_SYS_CLK BIT8 +#define SPI1_CLK_EQU_SYS_CLK BIT9 +#define PERIPHS_IO_MUX_MTDI_U (PERIPHS_IO_MUX + 0x04) +#define FUNC_GPIO12 3 +#define PERIPHS_IO_MUX_MTCK_U (PERIPHS_IO_MUX + 0x08) +#define FUNC_GPIO13 3 +#define PERIPHS_IO_MUX_MTMS_U (PERIPHS_IO_MUX + 0x0C) +#define FUNC_GPIO14 3 +#define PERIPHS_IO_MUX_MTDO_U (PERIPHS_IO_MUX + 0x10) +#define FUNC_GPIO15 3 +#define FUNC_U0RTS 4 +#define PERIPHS_IO_MUX_U0RXD_U (PERIPHS_IO_MUX + 0x14) +#define FUNC_U0RXD 0 +#define FUNC_GPIO3 3 +#define PERIPHS_IO_MUX_U0TXD_U (PERIPHS_IO_MUX + 0x18) +#define FUNC_U0TXD 0 +#define FUNC_GPIO1 3 +#define PERIPHS_IO_MUX_SD_CLK_U (PERIPHS_IO_MUX + 0x1c) +#define FUNC_SDCLK 0 +#define FUNC_SPICLK 1 +#define PERIPHS_IO_MUX_SD_DATA0_U (PERIPHS_IO_MUX + 0x20) +#define FUNC_SDDATA0 0 +#define FUNC_SPIQ 1 +#define FUNC_U1TXD 4 +#define PERIPHS_IO_MUX_SD_DATA1_U (PERIPHS_IO_MUX + 0x24) +#define FUNC_SDDATA1 0 +#define FUNC_SPID 1 +#define FUNC_U1RXD 4 +#define FUNC_SDDATA1_U1RXD 7 +#define PERIPHS_IO_MUX_SD_DATA2_U (PERIPHS_IO_MUX + 0x28) +#define FUNC_SDDATA2 0 +#define FUNC_SPIHD 1 +#define FUNC_GPIO9 3 +#define PERIPHS_IO_MUX_SD_DATA3_U (PERIPHS_IO_MUX + 0x2c) +#define FUNC_SDDATA3 0 +#define FUNC_SPIWP 1 +#define FUNC_GPIO10 3 +#define PERIPHS_IO_MUX_SD_CMD_U (PERIPHS_IO_MUX + 0x30) +#define FUNC_SDCMD 0 +#define FUNC_SPICS0 1 +#define PERIPHS_IO_MUX_GPIO0_U (PERIPHS_IO_MUX + 0x34) +#define FUNC_GPIO0 0 +#define PERIPHS_IO_MUX_GPIO2_U (PERIPHS_IO_MUX + 0x38) +#define FUNC_GPIO2 0 +#define FUNC_U1TXD_BK 2 +#define FUNC_U0TXD_BK 4 +#define PERIPHS_IO_MUX_GPIO4_U (PERIPHS_IO_MUX + 0x3C) +#define FUNC_GPIO4 0 +#define PERIPHS_IO_MUX_GPIO5_U (PERIPHS_IO_MUX + 0x40) +#define FUNC_GPIO5 0 + +#define PIN_PULLUP_DIS(PIN_NAME) CLEAR_PERI_REG_MASK(PIN_NAME, PERIPHS_IO_MUX_PULLUP) +#define PIN_PULLUP_EN(PIN_NAME) SET_PERI_REG_MASK(PIN_NAME, PERIPHS_IO_MUX_PULLUP) +#define PIN_PULLDWN_DIS(PIN_NAME) CLEAR_PERI_REG_MASK(PIN_NAME, PERIPHS_IO_MUX_PULLDWN) +#define PIN_PULLDWN_EN(PIN_NAME) SET_PERI_REG_MASK(PIN_NAME, PERIPHS_IO_MUX_PULLDWN) +#define PIN_FUNC_SELECT(PIN_NAME, FUNC) do { \ + CLEAR_PERI_REG_MASK(PIN_NAME, (PERIPHS_IO_MUX_FUNC<= GPIO_ID_PIN0) && (reg_id <= GPIO_ID_PIN(GPIO_PIN_COUNT-1))) + +#define GPIO_REGID_TO_PINIDX(reg_id) ((reg_id) - GPIO_ID_PIN0) + +typedef enum { + GPIO_PIN_INTR_DISABLE = 0, + GPIO_PIN_INTR_POSEDGE = 1, + GPIO_PIN_INTR_NEGEDGE = 2, + GPIO_PIN_INTR_ANYEGDE = 3, + GPIO_PIN_INTR_LOLEVEL = 4, + GPIO_PIN_INTR_HILEVEL = 5 +} GPIO_INT_TYPE; + +#define GPIO_OUTPUT_SET(gpio_no, bit_value) \ + gpio_output_set(bit_value<>gpio_no)&BIT0) + +/* GPIO interrupt handler, registered through gpio_intr_handler_register */ +typedef void (* gpio_intr_handler_fn_t)(uint32 intr_mask, void *arg); + + +/* + * Initialize GPIO. This includes reading the GPIO Configuration DataSet + * to initialize "output enables" and pin configurations for each gpio pin. + * Must be called once during startup. + */ +void gpio_init(void); + +/* + * Change GPIO pin output by setting, clearing, or disabling pins. + * In general, it is expected that a bit will be set in at most one + * of these masks. If a bit is clear in all masks, the output state + * remains unchanged. + * + * There is no particular ordering guaranteed; so if the order of + * writes is significant, calling code should divide a single call + * into multiple calls. + */ +void gpio_output_set(uint32 set_mask, + uint32 clear_mask, + uint32 enable_mask, + uint32 disable_mask); + +/* + * Sample the value of GPIO input pins and returns a bitmask. + */ +uint32 gpio_input_get(void); + +/* + * Set the specified GPIO register to the specified value. + * This is a very general and powerful interface that is not + * expected to be used during normal operation. It is intended + * mainly for debug, or for unusual requirements. + */ +void gpio_register_set(uint32 reg_id, uint32 value); + +/* Get the current value of the specified GPIO register. */ +uint32 gpio_register_get(uint32 reg_id); + +/* + * Register an application-specific interrupt handler for GPIO pin + * interrupts. Once the interrupt handler is called, it will not + * be called again until after a call to gpio_intr_ack. Any GPIO + * interrupts that occur during the interim are masked. + * + * The application-specific handler is called with a mask of + * pending GPIO interrupts. After processing pin interrupts, the + * application-specific handler may wish to use gpio_intr_pending + * to check for any additional pending interrupts before it returns. + */ +void gpio_intr_handler_register(gpio_intr_handler_fn_t fn, void *arg); + +/* Determine which GPIO interrupts are pending. */ +uint32 gpio_intr_pending(void); + +/* + * Acknowledge GPIO interrupts. + * Intended to be called from the gpio_intr_handler_fn. + */ +void gpio_intr_ack(uint32 ack_mask); + +void gpio_pin_wakeup_enable(uint32 i, GPIO_INT_TYPE intr_state); + +void gpio_pin_wakeup_disable(); + +void gpio_pin_intr_state_set(uint32 i, GPIO_INT_TYPE intr_state); + +#endif // _GPIO_H_ diff --git a/include/ip_addr.h b/include/ip_addr.h new file mode 100644 index 00000000..bd757a49 --- /dev/null +++ b/include/ip_addr.h @@ -0,0 +1,64 @@ +#ifndef __IP_ADDR_H__ +#define __IP_ADDR_H__ + +#include "c_types.h" + +struct ip_addr { + uint32 addr; +}; + +typedef struct ip_addr ip_addr_t; + +struct ip_info { + struct ip_addr ip; + struct ip_addr netmask; + struct ip_addr gw; +}; + +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = ((uint32)((d) & 0xff) << 24) | \ + ((uint32)((c) & 0xff) << 16) | \ + ((uint32)((b) & 0xff) << 8) | \ + (uint32)((a) & 0xff) + +/** + * Determine if two address are on the same network. + * + * @arg addr1 IP address 1 + * @arg addr2 IP address 2 + * @arg mask network identifier mask + * @return !0 if the network identifiers of both address match + */ +#define ip_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \ + (mask)->addr) == \ + ((addr2)->addr & \ + (mask)->addr)) + +/** Set an IP address given by the four byte-parts. + Little-endian version that prevents the use of htonl. */ +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = ((uint32)((d) & 0xff) << 24) | \ + ((uint32)((c) & 0xff) << 16) | \ + ((uint32)((b) & 0xff) << 8) | \ + (uint32)((a) & 0xff) + +#define ip4_addr1(ipaddr) (((uint8*)(ipaddr))[0]) +#define ip4_addr2(ipaddr) (((uint8*)(ipaddr))[1]) +#define ip4_addr3(ipaddr) (((uint8*)(ipaddr))[2]) +#define ip4_addr4(ipaddr) (((uint8*)(ipaddr))[3]) + +#define ip4_addr1_16(ipaddr) ((uint16)ip4_addr1(ipaddr)) +#define ip4_addr2_16(ipaddr) ((uint16)ip4_addr2(ipaddr)) +#define ip4_addr3_16(ipaddr) ((uint16)ip4_addr3(ipaddr)) +#define ip4_addr4_16(ipaddr) ((uint16)ip4_addr4(ipaddr)) + +uint32 ipaddr_addr(const char *cp); + +#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" + +#endif /* __IP_ADDR_H__ */ diff --git a/include/json/json.h b/include/json/json.h new file mode 100644 index 00000000..2308b5b7 --- /dev/null +++ b/include/json/json.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * A few JSON defines used for parsing and generating JSON. + * \author + * Niclas Finne + * Joakim Eriksson + */ + +#ifndef __JSON_H__ +#define __JSON_H__ + +#define JSON_TYPE_ARRAY '[' +#define JSON_TYPE_OBJECT '{' +#define JSON_TYPE_PAIR ':' +#define JSON_TYPE_PAIR_NAME 'N' /* for N:V pairs */ +#define JSON_TYPE_STRING '"' +#define JSON_TYPE_INT 'I' +#define JSON_TYPE_NUMBER '0' +#define JSON_TYPE_ERROR 0 + +/* how should we handle null vs false - both can be 0? */ +#define JSON_TYPE_NULL 'n' +#define JSON_TYPE_TRUE 't' +#define JSON_TYPE_FALSE 'f' + +#define JSON_TYPE_CALLBACK 'C' + +enum { + JSON_ERROR_OK, + JSON_ERROR_SYNTAX, + JSON_ERROR_UNEXPECTED_ARRAY, + JSON_ERROR_UNEXPECTED_END_OF_ARRAY, + JSON_ERROR_UNEXPECTED_OBJECT, + JSON_ERROR_UNEXPECTED_STRING +}; + +#define JSON_CONTENT_TYPE "application/json" + +#endif /* __JSON_H__ */ diff --git a/include/json/jsonparse.h b/include/json/jsonparse.h new file mode 100644 index 00000000..e1cb67a4 --- /dev/null +++ b/include/json/jsonparse.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +#ifndef __JSONPARSE_H__ +#define __JSONPARSE_H__ + +#include "c_types.h" +#include "json/json.h" + +#ifdef JSONPARSE_CONF_MAX_DEPTH +#define JSONPARSE_MAX_DEPTH JSONPARSE_CONF_MAX_DEPTH +#else +#define JSONPARSE_MAX_DEPTH 10 +#endif + +struct jsonparse_state { + const char *json; + int pos; + int len; + int depth; + /* for handling atomic values */ + int vstart; + int vlen; + char vtype; + char error; + char stack[JSONPARSE_MAX_DEPTH]; +}; + +/** + * \brief Initialize a JSON parser state. + * \param state A pointer to a JSON parser state + * \param json The string to parse as JSON + * \param len The length of the string to parse + * + * This function initializes a JSON parser state for + * parsing a string as JSON. + */ +void jsonparse_setup(struct jsonparse_state *state, const char *json, + int len); + +/* move to next JSON element */ +int jsonparse_next(struct jsonparse_state *state); + +/* copy the current JSON value into the specified buffer */ +int jsonparse_copy_value(struct jsonparse_state *state, char *buf, + int buf_size); + +/* get the current JSON value parsed as an int */ +int jsonparse_get_value_as_int(struct jsonparse_state *state); + +/* get the current JSON value parsed as a long */ +long jsonparse_get_value_as_long(struct jsonparse_state *state); + +/* get the current JSON value parsed as a unsigned long */ +unsigned long jsonparse_get_value_as_ulong(struct jsonparse_state *state); + +/* get the length of the current JSON value */ +int jsonparse_get_len(struct jsonparse_state *state); + +/* get the type of the current JSON value */ +int jsonparse_get_type(struct jsonparse_state *state); + +/* compare the JSON value with the specified string */ +int jsonparse_strcmp_value(struct jsonparse_state *state, const char *str); + +#endif /* __JSONPARSE_H__ */ diff --git a/include/json/jsontree.h b/include/json/jsontree.h new file mode 100644 index 00000000..0ffe9d15 --- /dev/null +++ b/include/json/jsontree.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * JSON output generation + * \author + * Niclas Finne + * Joakim Eriksson + */ + +#ifndef __JSONTREE_H__ +#define __JSONTREE_H__ + +#include "c_types.h" +#include "json/json.h" + +#ifdef JSONTREE_CONF_MAX_DEPTH +#define JSONTREE_MAX_DEPTH JSONTREE_CONF_MAX_DEPTH +#else +#define JSONTREE_MAX_DEPTH 10 +#endif /* JSONTREE_CONF_MAX_DEPTH */ + +struct jsontree_context { + struct jsontree_value *values[JSONTREE_MAX_DEPTH]; + uint16_t index[JSONTREE_MAX_DEPTH]; + int (* putchar)(int); + uint8_t depth; + uint8_t path; + int callback_state; +}; + +struct jsontree_value { + uint8_t type; + /* followed by a value */ +}; + +struct jsontree_string { + uint8_t type; + const char *value; +}; + +struct jsontree_int { + uint8_t type; + int value; +}; + +/* NOTE: the jsontree_callback set will receive a jsonparse state */ +struct jsonparse_state; +struct jsontree_callback { + uint8_t type; + int (* output)(struct jsontree_context *js_ctx); + int (* set)(struct jsontree_context *js_ctx, struct jsonparse_state *parser); +}; + +struct jsontree_pair { + const char *name; + struct jsontree_value *value; +}; + +struct jsontree_object { + uint8_t type; + uint8_t count; + struct jsontree_pair *pairs; +}; + +struct jsontree_array { + uint8_t type; + uint8_t count; + struct jsontree_value **values; +}; + +#define JSONTREE_STRING(text) {JSON_TYPE_STRING, (text)} +#define JSONTREE_PAIR(name, value) {(name), (struct jsontree_value *)(value)} +#define JSONTREE_CALLBACK(output, set) {JSON_TYPE_CALLBACK, (output), (set)} + +#define JSONTREE_OBJECT(name, ...) \ + static struct jsontree_pair jsontree_pair_##name[] = {__VA_ARGS__}; \ + static struct jsontree_object name = { \ + JSON_TYPE_OBJECT, \ + sizeof(jsontree_pair_##name)/sizeof(struct jsontree_pair), \ + jsontree_pair_##name } + +#define JSONTREE_PAIR_ARRAY(value) (struct jsontree_value *)(value) +#define JSONTREE_ARRAY(name, ...) \ + static struct jsontree_value* jsontree_value_##name[] = {__VA_ARGS__}; \ + static struct jsontree_array name = { \ + JSON_TYPE_ARRAY, \ + sizeof(jsontree_value_##name)/sizeof(struct jsontree_value*), \ + jsontree_value_##name } + +#define JSONTREE_OBJECT_EXT(name, ...) \ + static struct jsontree_pair jsontree_pair_##name[] = {__VA_ARGS__}; \ + struct jsontree_object name = { \ + JSON_TYPE_OBJECT, \ + sizeof(jsontree_pair_##name)/sizeof(struct jsontree_pair), \ + jsontree_pair_##name } + +void jsontree_setup(struct jsontree_context *js_ctx, + struct jsontree_value *root, int (* putchar)(int)); +void jsontree_reset(struct jsontree_context *js_ctx); + +const char *jsontree_path_name(const struct jsontree_context *js_ctx, + int depth); + +void jsontree_write_int(const struct jsontree_context *js_ctx, int value); +void jsontree_write_int_array(const struct jsontree_context *js_ctx, const int *text, uint32 length); + +void jsontree_write_atom(const struct jsontree_context *js_ctx, + const char *text); +void jsontree_write_string(const struct jsontree_context *js_ctx, + const char *text); +int jsontree_print_next(struct jsontree_context *js_ctx); +struct jsontree_value *jsontree_find_next(struct jsontree_context *js_ctx, + int type); + +#endif /* __JSONTREE_H__ */ diff --git a/include/mem.h b/include/mem.h new file mode 100644 index 00000000..b3fb8f48 --- /dev/null +++ b/include/mem.h @@ -0,0 +1,12 @@ +#ifndef __MEM_H__ +#define __MEM_H__ + +//void *pvPortMalloc( size_t xWantedSize ); +//void vPortFree( void *pv ); +//void *pvPortZalloc(size_t size); + +#define os_malloc pvPortMalloc +#define os_free vPortFree +#define os_zalloc pvPortZalloc + +#endif diff --git a/include/os_type.h b/include/os_type.h new file mode 100644 index 00000000..a9901061 --- /dev/null +++ b/include/os_type.h @@ -0,0 +1,19 @@ +/* + * copyright (c) Espressif System 2010 + * + * mapping to ETS structures + * + */ +#ifndef _OS_TYPES_H_ +#define _OS_TYPES_H_ + +#include "ets_sys.h" + +#define os_signal_t ETSSignal +#define os_param_t ETSParam +#define os_event_t ETSEvent +#define os_task_t ETSTask +#define os_timer_t ETSTimer +#define os_timer_func_t ETSTimerFunc + +#endif diff --git a/include/osapi.h b/include/osapi.h new file mode 100644 index 00000000..055085f0 --- /dev/null +++ b/include/osapi.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010 Espressif System + */ + +#ifndef _OSAPI_H_ +#define _OSAPI_H_ + +#include +#include "user_config.h" + +#define os_bzero ets_bzero +#define os_delay_us ets_delay_us +#define os_install_putc1 ets_install_putc1 +#define os_install_putc2 ets_install_putc2 +#define os_intr_lock ets_intr_lock +#define os_intr_unlock ets_intr_unlock +#define os_isr_attach ets_isr_attach +#define os_isr_mask ets_isr_mask +#define os_isr_unmask ets_isr_unmask +#define os_memcmp ets_memcmp +#define os_memcpy ets_memcpy +#define os_memmove ets_memmove +#define os_memset ets_memset +#define os_putc ets_putc +#define os_str2macaddr ets_str2macaddr +#define os_strcat strcat +#define os_strchr strchr +#define os_strcmp ets_strcmp +#define os_strcpy ets_strcpy +#define os_strlen ets_strlen +#define os_strncmp ets_strncmp +#define os_strncpy ets_strncpy +#define os_strstr ets_strstr +#ifdef USE_US_TIMER +#define os_timer_arm_us(a, b, c) ets_timer_arm_new(a, b, c, 0) +#endif +#define os_timer_arm(a, b, c) ets_timer_arm_new(a, b, c, 1) +#define os_timer_disarm ets_timer_disarm +#define os_timer_done ets_timer_done +#define os_timer_handler_isr ets_timer_handler_isr +#define os_timer_init ets_timer_init +#define os_timer_setfn ets_timer_setfn + +#define os_sprintf ets_sprintf +#define os_update_cpu_frequency ets_update_cpu_frequency + +#endif + diff --git a/include/queue.h b/include/queue.h new file mode 100644 index 00000000..a760c8db --- /dev/null +++ b/include/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/include/spi_flash.h b/include/spi_flash.h new file mode 100644 index 00000000..12dd6e17 --- /dev/null +++ b/include/spi_flash.h @@ -0,0 +1,31 @@ +/* + * copyright (c) Espressif System 2010 + * + */ + +#ifndef SPI_FLASH_H +#define SPI_FLASH_H + +typedef enum { + SPI_FLASH_RESULT_OK, + SPI_FLASH_RESULT_ERR, + SPI_FLASH_RESULT_TIMEOUT +} SpiFlashOpResult; + +typedef struct{ + uint32 deviceId; + uint32 chip_size; // chip size in byte + uint32 block_size; + uint32 sector_size; + uint32 page_size; + uint32 status_mask; +} SpiFlashChip; + +#define SPI_FLASH_SEC_SIZE 4096 + +uint32 spi_flash_get_id(void); +SpiFlashOpResult spi_flash_erase_sector(uint16 sec); +SpiFlashOpResult spi_flash_write(uint32 des_addr, uint32 *src_addr, uint32 size); +SpiFlashOpResult spi_flash_read(uint32 src_addr, uint32 *des_addr, uint32 size); + +#endif diff --git a/include/upgrade.h b/include/upgrade.h new file mode 100644 index 00000000..3b6bb70f --- /dev/null +++ b/include/upgrade.h @@ -0,0 +1,51 @@ +#ifndef __UPGRADE_H__ +#define __UPGRADE_H__ + +#define SPI_FLASH_SEC_SIZE 4096 + +#define USER_BIN1 0x00 +#define USER_BIN2 0x01 + +#define UPGRADE_FLAG_IDLE 0x00 +#define UPGRADE_FLAG_START 0x01 +#define UPGRADE_FLAG_FINISH 0x02 + +#define UPGRADE_FW_BIN1 0x00 +#define UPGRADE_FW_BIN2 0x01 + +typedef void (*upgrade_states_check_callback)(void * arg); + +//#define UPGRADE_SSL_ENABLE + +struct upgrade_server_info { + uint8 ip[4]; + uint16 port; + + uint8 upgrade_flag; + + uint8 pre_version[16]; + uint8 upgrade_version[16]; + + uint32 check_times; + uint8 *url; + + upgrade_states_check_callback check_cb; + struct espconn *pespconn; +}; + +#define UPGRADE_FLAG_IDLE 0x00 +#define UPGRADE_FLAG_START 0x01 +#define UPGRADE_FLAG_FINISH 0x02 + +//bool system_upgrade_start(struct upgrade_server_info *server); +bool system_upgrade_start_ssl(struct upgrade_server_info *server); +void system_upgrade_init(); +void system_upgrade_deinit(); +bool system_upgrade(uint8 *data, uint16 len); + +#ifdef UPGRADE_SSL_ENABLE +bool system_upgrade_start_ssl(struct upgrade_server_info *server); +#else +bool system_upgrade_start(struct upgrade_server_info *server); +#endif +#endif diff --git a/include/user_interface.h b/include/user_interface.h new file mode 100644 index 00000000..8002faa5 --- /dev/null +++ b/include/user_interface.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2013 -2014 Espressif System + * + */ + +#ifndef __USER_INTERFACE_H__ +#define __USER_INTERFACE_H__ + +#include "os_type.h" +#ifdef LWIP_OPEN_SRC +#include "lwip/ip_addr.h" +#else +#include "ip_addr.h" +#endif + +#include "queue.h" +#include "user_config.h" +#include "spi_flash.h" + +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +enum rst_reason { + DEFAULT_RST_FLAG = 0, + WDT_RST_FLAG = 1, + EXP_RST_FLAG = 2 +}; + +struct rst_info{ + uint32 flag; + uint32 exccause; + uint32 epc1; + uint32 epc2; + uint32 epc3; + uint32 excvaddr; + uint32 depc; +}; + +#define UPGRADE_FW_BIN1 0x00 +#define UPGRADE_FW_BIN2 0x01 + +void system_restore(void); +void system_restart(void); +void system_deep_sleep(uint32 time_in_us); +uint8 system_upgrade_userbin_check(void); +void system_upgrade_reboot(void); +uint8 system_upgrade_flag_check(); +void system_upgrade_flag_set(uint8 flag); +void system_timer_reinit(void); +uint32 system_get_time(void); + +/* user task's prio must be 0/1/2 !!!*/ +enum { + USER_TASK_PRIO_0 = 0, + USER_TASK_PRIO_1, + USER_TASK_PRIO_2, + USER_TASK_PRIO_MAX +}; + +void system_os_task(os_task_t task, uint8 prio, os_event_t *queue, uint8 qlen); +void system_os_post(uint8 prio, os_signal_t sig, os_param_t par); + +void system_print_meminfo(void); +uint32 system_get_free_heap_size(void); + +void system_set_os_print(uint8 onoff); + +uint64 system_mktime(uint32 year, uint32 mon, uint32 day, uint32 hour, uint32 min, uint32 sec); + +uint32 system_get_chip_id(void); + +typedef void (* init_done_cb_t)(void); + +void system_init_done_cb(init_done_cb_t cb); + +uint32 system_rtc_clock_cali_proc(void); +uint32 system_get_rtc_time(void); + +bool system_rtc_mem_read(uint8 src_addr, void *des_addr, uint16 load_size); +bool system_rtc_mem_write(uint8 des_addr, const void *src_addr, uint16 save_size); + +void system_uart_swap(void); + +uint16 system_adc_read(void); + +const char *system_get_sdk_version(void); + +#define NULL_MODE 0x00 +#define STATION_MODE 0x01 +#define SOFTAP_MODE 0x02 +#define STATIONAP_MODE 0x03 + +uint8 wifi_get_opmode(void); +bool wifi_set_opmode(uint8 opmode); + +struct bss_info { + STAILQ_ENTRY(bss_info) next; + + uint8 bssid[6]; + uint8 ssid[32]; + uint8 channel; + sint8 rssi; + uint8 authmode; + uint8 is_hidden; +}; + +typedef struct _scaninfo { + STAILQ_HEAD(, bss_info) *pbss; + struct espconn *pespconn; + uint8 totalpage; + uint8 pagenum; + uint8 page_sn; + uint8 data_cnt; +} scaninfo; + +typedef void (* scan_done_cb_t)(void *arg, STATUS status); + +struct station_config { + uint8 ssid[32]; + uint8 password[64]; + uint8 bssid_set; + uint8 bssid[6]; +}; + +bool wifi_station_get_config(struct station_config *config); +bool wifi_station_set_config(struct station_config *config); + +bool wifi_station_connect(void); +bool wifi_station_disconnect(void); + +struct scan_config { + uint8 *ssid; + uint8 *bssid; + uint8 channel; + uint8 show_hidden; +}; + +bool wifi_station_scan(struct scan_config *config, scan_done_cb_t cb); + +uint8 wifi_station_get_auto_connect(void); +bool wifi_station_set_auto_connect(uint8 set); + +enum { + STATION_IDLE = 0, + STATION_CONNECTING, + STATION_WRONG_PASSWORD, + STATION_NO_AP_FOUND, + STATION_CONNECT_FAIL, + STATION_GOT_IP +}; + +enum dhcp_status{ + DHCP_ENABLE, + DHCP_DISABLE +}; + +uint8 wifi_station_get_connect_status(void); + +uint8 wifi_station_get_current_ap_id(void); +bool wifi_station_ap_change(uint8 current_ap_id); +bool wifi_station_ap_number_set(uint8 ap_number); + +bool wifi_station_dhcpc_start(void); +bool wifi_station_dhcpc_stop(void); +enum dhcp_status wifi_station_dhcpc_status(void); + +typedef enum _auth_mode { + AUTH_OPEN = 0, + AUTH_WEP, + AUTH_WPA_PSK, + AUTH_WPA2_PSK, + AUTH_WPA_WPA2_PSK +} AUTH_MODE; + +struct softap_config { + uint8 ssid[32]; + uint8 password[64]; + uint8 ssid_len; + uint8 channel; + uint8 authmode; + uint8 ssid_hidden; + uint8 max_connection; +}; + +bool wifi_softap_get_config(struct softap_config *config); +bool wifi_softap_set_config(struct softap_config *config); + +struct station_info { + STAILQ_ENTRY(station_info) next; + + uint8 bssid[6]; + struct ip_addr ip; +}; + +struct dhcps_lease { + uint32 start_ip; + uint32 end_ip; +}; + +struct station_info * wifi_softap_get_station_info(void); +void wifi_softap_free_station_info(void); +bool wifi_station_get_ap_info(struct station_config config[]); + +bool wifi_softap_dhcps_start(void); +bool wifi_softap_dhcps_stop(void); +bool wifi_softap_set_dhcps_lease(struct dhcps_lease *please); +enum dhcp_status wifi_softap_dhcps_status(void); + +#define STATION_IF 0x00 +#define SOFTAP_IF 0x01 + +bool wifi_get_ip_info(uint8 if_index, struct ip_info *info); +bool wifi_set_ip_info(uint8 if_index, struct ip_info *info); +bool wifi_get_macaddr(uint8 if_index, uint8 *macaddr); +bool wifi_set_macaddr(uint8 if_index, uint8 *macaddr); + +uint8 wifi_get_channel(void); +bool wifi_set_channel(uint8 channel); + +void wifi_status_led_install(uint8 gpio_id, uint32 gpio_name, uint8 gpio_func); + +/** Get the absolute difference between 2 u32_t values (correcting overflows) + * 'a' is expected to be 'higher' (without overflow) than 'b'. */ +#define ESP_U32_DIFF(a, b) (((a) >= (b)) ? ((a) - (b)) : (((a) + ((b) ^ 0xFFFFFFFF) + 1))) + +void wifi_promiscuous_enable(uint8 promiscuous); + +typedef void (* wifi_promiscuous_cb_t)(uint8 *buf, uint16 len); + +void wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb); + +enum phy_mode { + PHY_MODE_11B = 1, + PHY_MODE_11G = 2, + PHY_MODE_11N = 3 +}; + +enum phy_mode wifi_get_phy_mode(void); +bool wifi_set_phy_mode(enum phy_mode mode); + +enum sleep_type { + NONE_SLEEP_T = 0, + LIGHT_SLEEP_T, + MODEM_SLEEP_T +}; + +bool wifi_set_sleep_type(enum sleep_type type); +enum sleep_type wifi_get_sleep_type(void); + +#endif diff --git a/ld/eagle.app.v6.app1.ld b/ld/eagle.app.v6.app1.ld new file mode 100644 index 00000000..bb321ff8 --- /dev/null +++ b/ld/eagle.app.v6.app1.ld @@ -0,0 +1,181 @@ +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 + irom0_0_seg : org = 0x40211000, len = 0x2B000 +} + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(call_user_start) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _irom0_text_start = ABSOLUTE(.); + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + _irom0_text_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr +} + +/* get ROM code address */ +INCLUDE "../ld/eagle.rom.addr.v6.ld" diff --git a/ld/eagle.app.v6.app2.ld b/ld/eagle.app.v6.app2.ld new file mode 100644 index 00000000..2693a99e --- /dev/null +++ b/ld/eagle.app.v6.app2.ld @@ -0,0 +1,181 @@ +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 + irom0_0_seg : org = 0x40251000, len = 0x2B000 +} + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(call_user_start) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _irom0_text_start = ABSOLUTE(.); + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + _irom0_text_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr +} + +/* get ROM code address */ +INCLUDE "../ld/eagle.rom.addr.v6.ld" diff --git a/ld/eagle.app.v6.ld b/ld/eagle.app.v6.ld new file mode 100644 index 00000000..e3a1d223 --- /dev/null +++ b/ld/eagle.app.v6.ld @@ -0,0 +1,185 @@ +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 + irom0_0_seg : org = 0x40210000, len = 0x50000 +} + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(call_user_start) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _crypt_fixed_start = ABSOLUTE(.); + *(.crypt_fixed) + _crypt_fixed_end = ABSOLUTE(.); + _irom0_text_start = ABSOLUTE(.); + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + _irom0_text_end = ABSOLUTE(.); + _flash_used_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr +} + +/* get ROM code address */ +INCLUDE "../ld/eagle.rom.addr.v6.ld" diff --git a/ld/eagle.rom.addr.v6.ld b/ld/eagle.rom.addr.v6.ld new file mode 100644 index 00000000..c5c1b652 --- /dev/null +++ b/ld/eagle.rom.addr.v6.ld @@ -0,0 +1,344 @@ +PROVIDE ( Cache_Read_Disable = 0x400047f0 ); +PROVIDE ( Cache_Read_Enable = 0x40004678 ); +PROVIDE ( FilePacketSendReqMsgProc = 0x400035a0 ); +PROVIDE ( FlashDwnLdParamCfgMsgProc = 0x4000368c ); +PROVIDE ( FlashDwnLdStartMsgProc = 0x40003538 ); +PROVIDE ( FlashDwnLdStopReqMsgProc = 0x40003658 ); +PROVIDE ( GetUartDevice = 0x40003f4c ); +PROVIDE ( MD5Final = 0x40009900 ); +PROVIDE ( MD5Init = 0x40009818 ); +PROVIDE ( MD5Update = 0x40009834 ); +PROVIDE ( MemDwnLdStartMsgProc = 0x400036c4 ); +PROVIDE ( MemDwnLdStopReqMsgProc = 0x4000377c ); +PROVIDE ( MemPacketSendReqMsgProc = 0x400036f0 ); +PROVIDE ( RcvMsg = 0x40003eac ); +PROVIDE ( SHA1Final = 0x4000b648 ); +PROVIDE ( SHA1Init = 0x4000b584 ); +PROVIDE ( SHA1Transform = 0x4000a364 ); +PROVIDE ( SHA1Update = 0x4000b5a8 ); +PROVIDE ( Wait_SPI_Idle = 0x4000448c ); +PROVIDE ( SPIEraseArea = 0x40004b44 ); +PROVIDE ( SPIEraseBlock = 0x400049b4 ); +PROVIDE ( SPIEraseChip = 0x40004984 ); +PROVIDE ( SPIEraseSector = 0x40004a00 ); +PROVIDE ( SPILock = 0x400048a8 ); +PROVIDE ( SPIParamCfg = 0x40004c2c ); +PROVIDE ( SPIRead = 0x40004b1c ); +PROVIDE ( SPIReadModeCnfig = 0x400048ec ); +PROVIDE ( SPIUnlock = 0x40004878 ); +PROVIDE ( SPIWrite = 0x40004a4c ); +PROVIDE ( SelectSpiFunction = 0x40003f58 ); +PROVIDE ( SendMsg = 0x40003cf4 ); +PROVIDE ( UartConnCheck = 0x40003230 ); +PROVIDE ( UartConnectProc = 0x400037a0 ); +PROVIDE ( UartDwnLdProc = 0x40003368 ); +PROVIDE ( UartGetCmdLn = 0x40003ef4 ); +PROVIDE ( UartRegReadProc = 0x4000381c ); +PROVIDE ( UartRegWriteProc = 0x400037ac ); +PROVIDE ( UartRxString = 0x40003c30 ); +PROVIDE ( Uart_Init = 0x40003a14 ); +PROVIDE ( _DebugExceptionVector = 0x40000010 ); +PROVIDE ( _DoubleExceptionVector = 0x40000070 ); +PROVIDE ( _KernelExceptionVector = 0x40000030 ); +PROVIDE ( _NMIExceptionVector = 0x40000020 ); +PROVIDE ( _ResetHandler = 0x400000a4 ); +PROVIDE ( _ResetVector = 0x40000080 ); +PROVIDE ( _UserExceptionVector = 0x40000050 ); +PROVIDE ( __adddf3 = 0x4000c538 ); +PROVIDE ( __addsf3 = 0x4000c180 ); +PROVIDE ( __divdf3 = 0x4000cb94 ); +PROVIDE ( __divdi3 = 0x4000ce60 ); +PROVIDE ( __divsi3 = 0x4000dc88 ); +PROVIDE ( __extendsfdf2 = 0x4000cdfc ); +PROVIDE ( __fixdfsi = 0x4000ccb8 ); +PROVIDE ( __fixunsdfsi = 0x4000cd00 ); +PROVIDE ( __fixunssfsi = 0x4000c4c4 ); +PROVIDE ( __floatsidf = 0x4000e2f0 ); +PROVIDE ( __floatsisf = 0x4000e2ac ); +PROVIDE ( __floatunsidf = 0x4000e2e8 ); +PROVIDE ( __floatunsisf = 0x4000e2a4 ); +PROVIDE ( __muldf3 = 0x4000c8f0 ); +PROVIDE ( __muldi3 = 0x40000650 ); +PROVIDE ( __mulsf3 = 0x4000c3dc ); +PROVIDE ( __subdf3 = 0x4000c688 ); +PROVIDE ( __subsf3 = 0x4000c268 ); +PROVIDE ( __truncdfsf2 = 0x4000cd5c ); +PROVIDE ( __udivdi3 = 0x4000d310 ); +PROVIDE ( __udivsi3 = 0x4000e21c ); +PROVIDE ( __umoddi3 = 0x4000d770 ); +PROVIDE ( __umodsi3 = 0x4000e268 ); +PROVIDE ( __umulsidi3 = 0x4000dcf0 ); +PROVIDE ( _rom_store = 0x4000e388 ); +PROVIDE ( _rom_store_table = 0x4000e328 ); +PROVIDE ( _start = 0x4000042c ); +PROVIDE ( _xtos_alloca_handler = 0x4000dbe0 ); +PROVIDE ( _xtos_c_wrapper_handler = 0x40000598 ); +PROVIDE ( _xtos_cause3_handler = 0x40000590 ); +PROVIDE ( _xtos_ints_off = 0x4000bda4 ); +PROVIDE ( _xtos_ints_on = 0x4000bd84 ); +PROVIDE ( _xtos_l1int_handler = 0x4000048c ); +PROVIDE ( _xtos_p_none = 0x4000dbf8 ); +PROVIDE ( _xtos_restore_intlevel = 0x4000056c ); +PROVIDE ( _xtos_return_from_exc = 0x4000dc54 ); +PROVIDE ( _xtos_set_exception_handler = 0x40000454 ); +PROVIDE ( _xtos_set_interrupt_handler = 0x4000bd70 ); +PROVIDE ( _xtos_set_interrupt_handler_arg = 0x4000bd28 ); +PROVIDE ( _xtos_set_intlevel = 0x4000dbfc ); +PROVIDE ( _xtos_set_min_intlevel = 0x4000dc18 ); +PROVIDE ( _xtos_set_vpri = 0x40000574 ); +PROVIDE ( _xtos_syscall_handler = 0x4000dbe4 ); +PROVIDE ( _xtos_unhandled_exception = 0x4000dc44 ); +PROVIDE ( _xtos_unhandled_interrupt = 0x4000dc3c ); +PROVIDE ( aes_decrypt = 0x400092d4 ); +PROVIDE ( aes_decrypt_deinit = 0x400092e4 ); +PROVIDE ( aes_decrypt_init = 0x40008ea4 ); +PROVIDE ( aes_unwrap = 0x40009410 ); +PROVIDE ( base64_decode = 0x40009648 ); +PROVIDE ( base64_encode = 0x400094fc ); +PROVIDE ( bzero = 0x4000de84 ); +PROVIDE ( cmd_parse = 0x40000814 ); +PROVIDE ( conv_str_decimal = 0x40000b24 ); +PROVIDE ( conv_str_hex = 0x40000cb8 ); +PROVIDE ( convert_para_str = 0x40000a60 ); +PROVIDE ( dtm_get_intr_mask = 0x400026d0 ); +PROVIDE ( dtm_params_init = 0x4000269c ); +PROVIDE ( dtm_set_intr_mask = 0x400026c8 ); +PROVIDE ( dtm_set_params = 0x400026dc ); +PROVIDE ( eprintf = 0x40001d14 ); +PROVIDE ( eprintf_init_buf = 0x40001cb8 ); +PROVIDE ( eprintf_to_host = 0x40001d48 ); +PROVIDE ( est_get_printf_buf_remain_len = 0x40002494 ); +PROVIDE ( est_reset_printf_buf_len = 0x4000249c ); +PROVIDE ( ets_bzero = 0x40002ae8 ); +PROVIDE ( ets_char2xdigit = 0x40002b74 ); +PROVIDE ( ets_delay_us = 0x40002ecc ); +PROVIDE ( ets_enter_sleep = 0x400027b8 ); +PROVIDE ( ets_external_printf = 0x40002578 ); +PROVIDE ( ets_get_cpu_frequency = 0x40002f0c ); +PROVIDE ( ets_getc = 0x40002bcc ); +PROVIDE ( ets_install_external_printf = 0x40002450 ); +PROVIDE ( ets_install_putc1 = 0x4000242c ); +PROVIDE ( ets_install_putc2 = 0x4000248c ); +PROVIDE ( ets_install_uart_printf = 0x40002438 ); +PROVIDE ( ets_intr_lock = 0x40000f74 ); +PROVIDE ( ets_intr_unlock = 0x40000f80 ); +PROVIDE ( ets_isr_attach = 0x40000f88 ); +PROVIDE ( ets_isr_mask = 0x40000f98 ); +PROVIDE ( ets_isr_unmask = 0x40000fa8 ); +PROVIDE ( ets_memcmp = 0x400018d4 ); +PROVIDE ( ets_memcpy = 0x400018b4 ); +PROVIDE ( ets_memmove = 0x400018c4 ); +PROVIDE ( ets_memset = 0x400018a4 ); +PROVIDE ( ets_post = 0x40000e24 ); +PROVIDE ( ets_printf = 0x400024cc ); +PROVIDE ( ets_putc = 0x40002be8 ); +PROVIDE ( ets_rtc_int_register = 0x40002a40 ); +PROVIDE ( ets_run = 0x40000e04 ); +PROVIDE ( ets_set_idle_cb = 0x40000dc0 ); +PROVIDE ( ets_set_user_start = 0x40000fbc ); +PROVIDE ( ets_str2macaddr = 0x40002af8 ); +PROVIDE ( ets_strcmp = 0x40002aa8 ); +PROVIDE ( ets_strcpy = 0x40002a88 ); +PROVIDE ( ets_strlen = 0x40002ac8 ); +PROVIDE ( ets_strncmp = 0x40002ab8 ); +PROVIDE ( ets_strncpy = 0x40002a98 ); +PROVIDE ( ets_strstr = 0x40002ad8 ); +PROVIDE ( ets_task = 0x40000dd0 ); +PROVIDE ( ets_timer_arm = 0x40002cc4 ); +PROVIDE ( ets_timer_disarm = 0x40002d40 ); +PROVIDE ( ets_timer_done = 0x40002d80 ); +PROVIDE ( ets_timer_handler_isr = 0x40002da8 ); +PROVIDE ( ets_timer_init = 0x40002e68 ); +PROVIDE ( ets_timer_setfn = 0x40002c48 ); +PROVIDE ( ets_uart_printf = 0x40002544 ); +PROVIDE ( ets_update_cpu_frequency = 0x40002f04 ); +PROVIDE ( ets_vprintf = 0x40001f00 ); +PROVIDE ( ets_wdt_disable = 0x400030f0 ); +PROVIDE ( ets_wdt_enable = 0x40002fa0 ); +PROVIDE ( ets_wdt_get_mode = 0x40002f34 ); +PROVIDE ( ets_wdt_init = 0x40003170 ); +PROVIDE ( ets_wdt_restore = 0x40003158 ); +PROVIDE ( ets_write_char = 0x40001da0 ); +PROVIDE ( get_first_seg = 0x4000091c ); +PROVIDE ( gpio_init = 0x40004c50 ); +PROVIDE ( gpio_input_get = 0x40004cf0 ); +PROVIDE ( gpio_intr_ack = 0x40004dcc ); +PROVIDE ( gpio_intr_handler_register = 0x40004e28 ); +PROVIDE ( gpio_intr_pending = 0x40004d88 ); +PROVIDE ( gpio_intr_test = 0x40004efc ); +PROVIDE ( gpio_output_set = 0x40004cd0 ); +PROVIDE ( gpio_pin_intr_state_set = 0x40004d90 ); +PROVIDE ( gpio_pin_wakeup_disable = 0x40004ed4 ); +PROVIDE ( gpio_pin_wakeup_enable = 0x40004e90 ); +PROVIDE ( gpio_register_get = 0x40004d5c ); +PROVIDE ( gpio_register_set = 0x40004d04 ); +PROVIDE ( hmac_md5 = 0x4000a2cc ); +PROVIDE ( hmac_md5_vector = 0x4000a160 ); +PROVIDE ( hmac_sha1 = 0x4000ba28 ); +PROVIDE ( hmac_sha1_vector = 0x4000b8b4 ); +PROVIDE ( lldesc_build_chain = 0x40004f40 ); +PROVIDE ( lldesc_num2link = 0x40005050 ); +PROVIDE ( lldesc_set_owner = 0x4000507c ); +PROVIDE ( main = 0x40000fec ); +PROVIDE ( md5_vector = 0x400097ac ); +PROVIDE ( mem_calloc = 0x40001c2c ); +PROVIDE ( mem_free = 0x400019e0 ); +PROVIDE ( mem_init = 0x40001998 ); +PROVIDE ( mem_malloc = 0x40001b40 ); +PROVIDE ( mem_realloc = 0x40001c6c ); +PROVIDE ( mem_trim = 0x40001a14 ); +PROVIDE ( mem_zalloc = 0x40001c58 ); +PROVIDE ( memcmp = 0x4000dea8 ); +PROVIDE ( memcpy = 0x4000df48 ); +PROVIDE ( memmove = 0x4000e04c ); +PROVIDE ( memset = 0x4000e190 ); +PROVIDE ( multofup = 0x400031c0 ); +PROVIDE ( pbkdf2_sha1 = 0x4000b840 ); +PROVIDE ( phy_get_romfuncs = 0x40006b08 ); +PROVIDE ( rand = 0x40000600 ); +PROVIDE ( rc4_skip = 0x4000dd68 ); +PROVIDE ( recv_packet = 0x40003d08 ); +PROVIDE ( remove_head_space = 0x40000a04 ); +PROVIDE ( rijndaelKeySetupDec = 0x40008dd0 ); +PROVIDE ( rijndaelKeySetupEnc = 0x40009300 ); +PROVIDE ( rom_abs_temp = 0x400060c0 ); +PROVIDE ( rom_ana_inf_gating_en = 0x40006b10 ); +PROVIDE ( rom_cal_tos_v50 = 0x40007a28 ); +PROVIDE ( rom_chip_50_set_channel = 0x40006f84 ); +PROVIDE ( rom_chip_v5_disable_cca = 0x400060d0 ); +PROVIDE ( rom_chip_v5_enable_cca = 0x400060ec ); +PROVIDE ( rom_chip_v5_rx_init = 0x4000711c ); +PROVIDE ( rom_chip_v5_sense_backoff = 0x4000610c ); +PROVIDE ( rom_chip_v5_tx_init = 0x4000718c ); +PROVIDE ( rom_dc_iq_est = 0x4000615c ); +PROVIDE ( rom_en_pwdet = 0x400061b8 ); +PROVIDE ( rom_get_bb_atten = 0x40006238 ); +PROVIDE ( rom_get_corr_power = 0x40006260 ); +PROVIDE ( rom_get_fm_sar_dout = 0x400062dc ); +PROVIDE ( rom_get_noisefloor = 0x40006394 ); +PROVIDE ( rom_get_power_db = 0x400063b0 ); +PROVIDE ( rom_i2c_readReg = 0x40007268 ); +PROVIDE ( rom_i2c_readReg_Mask = 0x4000729c ); +PROVIDE ( rom_i2c_writeReg = 0x400072d8 ); +PROVIDE ( rom_i2c_writeReg_Mask = 0x4000730c ); +PROVIDE ( rom_iq_est_disable = 0x40006400 ); +PROVIDE ( rom_iq_est_enable = 0x40006430 ); +PROVIDE ( rom_linear_to_db = 0x40006484 ); +PROVIDE ( rom_mhz2ieee = 0x400065a4 ); +PROVIDE ( rom_pbus_dco___SA2 = 0x40007bf0 ); +PROVIDE ( rom_pbus_debugmode = 0x4000737c ); +PROVIDE ( rom_pbus_enter_debugmode = 0x40007410 ); +PROVIDE ( rom_pbus_exit_debugmode = 0x40007448 ); +PROVIDE ( rom_pbus_force_test = 0x4000747c ); +PROVIDE ( rom_pbus_rd = 0x400074d8 ); +PROVIDE ( rom_pbus_set_rxgain = 0x4000754c ); +PROVIDE ( rom_pbus_set_txgain = 0x40007610 ); +PROVIDE ( rom_pbus_workmode = 0x40007648 ); +PROVIDE ( rom_pbus_xpd_rx_off = 0x40007688 ); +PROVIDE ( rom_pbus_xpd_rx_on = 0x400076cc ); +PROVIDE ( rom_pbus_xpd_tx_off = 0x400076fc ); +PROVIDE ( rom_pbus_xpd_tx_on = 0x40007740 ); +PROVIDE ( rom_pbus_xpd_tx_on__low_gain = 0x400077a0 ); +PROVIDE ( rom_phy_reset_req = 0x40007804 ); +PROVIDE ( rom_restart_cal = 0x4000781c ); +PROVIDE ( rom_rfcal_pwrctrl = 0x40007eb4 ); +PROVIDE ( rom_rfcal_rxiq = 0x4000804c ); +PROVIDE ( rom_rfcal_rxiq_set_reg = 0x40008264 ); +PROVIDE ( rom_rfcal_txcap = 0x40008388 ); +PROVIDE ( rom_rfcal_txiq = 0x40008610 ); +PROVIDE ( rom_rfcal_txiq_cover = 0x400088b8 ); +PROVIDE ( rom_rfcal_txiq_set_reg = 0x40008a70 ); +PROVIDE ( rom_rfpll_reset = 0x40007868 ); +PROVIDE ( rom_rfpll_set_freq = 0x40007968 ); +PROVIDE ( rom_rxiq_cover_mg_mp = 0x40008b6c ); +PROVIDE ( rom_rxiq_get_mis = 0x40006628 ); +PROVIDE ( rom_sar_init = 0x40006738 ); +PROVIDE ( rom_set_ana_inf_tx_scale = 0x4000678c ); +PROVIDE ( rom_set_channel_freq = 0x40006c50 ); +PROVIDE ( rom_set_loopback_gain = 0x400067c8 ); +PROVIDE ( rom_set_noise_floor = 0x40006830 ); +PROVIDE ( rom_set_rxclk_en = 0x40006550 ); +PROVIDE ( rom_set_txbb_atten = 0x40008c6c ); +PROVIDE ( rom_set_txclk_en = 0x4000650c ); +PROVIDE ( rom_set_txiq_cal = 0x40008d34 ); +PROVIDE ( rom_start_noisefloor = 0x40006874 ); +PROVIDE ( rom_start_tx_tone = 0x400068b4 ); +PROVIDE ( rom_stop_tx_tone = 0x4000698c ); +PROVIDE ( rom_tx_mac_disable = 0x40006a98 ); +PROVIDE ( rom_tx_mac_enable = 0x40006ad4 ); +PROVIDE ( rom_txtone_linear_pwr = 0x40006a1c ); +PROVIDE ( rom_write_rfpll_sdm = 0x400078dc ); +PROVIDE ( roundup2 = 0x400031b4 ); +PROVIDE ( rtc_enter_sleep = 0x40002870 ); +PROVIDE ( rtc_get_reset_reason = 0x400025e0 ); +PROVIDE ( rtc_intr_handler = 0x400029ec ); +PROVIDE ( rtc_set_sleep_mode = 0x40002668 ); +PROVIDE ( save_rxbcn_mactime = 0x400027a4 ); +PROVIDE ( save_tsf_us = 0x400027ac ); +PROVIDE ( send_packet = 0x40003c80 ); +PROVIDE ( sha1_prf = 0x4000ba48 ); +PROVIDE ( sha1_vector = 0x4000a2ec ); +PROVIDE ( sip_alloc_to_host_evt = 0x40005180 ); +PROVIDE ( sip_get_ptr = 0x400058a8 ); +PROVIDE ( sip_get_state = 0x40005668 ); +PROVIDE ( sip_init_attach = 0x4000567c ); +PROVIDE ( sip_install_rx_ctrl_cb = 0x4000544c ); +PROVIDE ( sip_install_rx_data_cb = 0x4000545c ); +PROVIDE ( sip_post = 0x400050fc ); +PROVIDE ( sip_post_init = 0x400056c4 ); +PROVIDE ( sip_reclaim_from_host_cmd = 0x4000534c ); +PROVIDE ( sip_reclaim_tx_data_pkt = 0x400052c0 ); +PROVIDE ( sip_send = 0x40005808 ); +PROVIDE ( sip_to_host_chain_append = 0x40005864 ); +PROVIDE ( sip_to_host_evt_send_done = 0x40005234 ); +PROVIDE ( slc_add_credits = 0x400060ac ); +PROVIDE ( slc_enable = 0x40005d90 ); +PROVIDE ( slc_from_host_chain_fetch = 0x40005f24 ); +PROVIDE ( slc_from_host_chain_recycle = 0x40005e94 ); +PROVIDE ( slc_init_attach = 0x40005c50 ); +PROVIDE ( slc_init_credit = 0x4000608c ); +PROVIDE ( slc_pause_from_host = 0x40006014 ); +PROVIDE ( slc_reattach = 0x40005c1c ); +PROVIDE ( slc_resume_from_host = 0x4000603c ); +PROVIDE ( slc_select_tohost_gpio = 0x40005dc0 ); +PROVIDE ( slc_select_tohost_gpio_mode = 0x40005db8 ); +PROVIDE ( slc_send_to_host_chain = 0x40005de4 ); +PROVIDE ( slc_set_host_io_max_window = 0x40006068 ); +PROVIDE ( slc_to_host_chain_recycle = 0x40005f10 ); +PROVIDE ( software_reset = 0x4000264c ); +PROVIDE ( spi_flash_attach = 0x40004644 ); +PROVIDE ( srand = 0x400005f0 ); +PROVIDE ( strcmp = 0x4000bdc8 ); +PROVIDE ( strcpy = 0x4000bec8 ); +PROVIDE ( strlen = 0x4000bf4c ); +PROVIDE ( strncmp = 0x4000bfa8 ); +PROVIDE ( strncpy = 0x4000c0a0 ); +PROVIDE ( strstr = 0x4000e1e0 ); +PROVIDE ( timer_insert = 0x40002c64 ); +PROVIDE ( uartAttach = 0x4000383c ); +PROVIDE ( uart_baudrate_detect = 0x40003924 ); +PROVIDE ( uart_buff_switch = 0x400038a4 ); +PROVIDE ( uart_div_modify = 0x400039d8 ); +PROVIDE ( uart_rx_intr_handler = 0x40003bbc ); +PROVIDE ( uart_rx_one_char = 0x40003b8c ); +PROVIDE ( uart_rx_one_char_block = 0x40003b64 ); +PROVIDE ( uart_rx_readbuff = 0x40003ec8 ); +PROVIDE ( uart_tx_one_char = 0x40003b30 ); +PROVIDE ( wepkey_128 = 0x4000bc40 ); +PROVIDE ( wepkey_64 = 0x4000bb3c ); +PROVIDE ( xthal_bcopy = 0x40000688 ); +PROVIDE ( xthal_copy123 = 0x4000074c ); +PROVIDE ( xthal_get_ccompare = 0x4000dd4c ); +PROVIDE ( xthal_get_ccount = 0x4000dd38 ); +PROVIDE ( xthal_get_interrupt = 0x4000dd58 ); +PROVIDE ( xthal_get_intread = 0x4000dd58 ); +PROVIDE ( xthal_memcpy = 0x400006c4 ); +PROVIDE ( xthal_set_ccompare = 0x4000dd40 ); +PROVIDE ( xthal_set_intclear = 0x4000dd60 ); +PROVIDE ( xthal_spill_registers_into_stack_nw = 0x4000e320 ); +PROVIDE ( xthal_window_spill = 0x4000e324 ); +PROVIDE ( xthal_window_spill_nw = 0x4000e320 ); + +PROVIDE ( Te0 = 0x3fffccf0 ); +PROVIDE ( UartDev = 0x3fffde10 ); +PROVIDE ( flashchip = 0x3fffc714); diff --git a/lib/libjson.a b/lib/libjson.a new file mode 100644 index 0000000000000000000000000000000000000000..0cd54651997d09a6a010a85e72344550d339d35a GIT binary patch literal 12714 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êMoHOpWj&b%j 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 no}GAJ;^rD(DH_r7a=lMDfG=l?s; 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

vQ4PVBz`V|98OOh2?fxUk6_~hW8K_ zmMjC|(_VXE;MZho&|$h1L%%To-W=SA7`Z^gha<~8df3Dqx5Tb@Vi3?nSe zuz5|WbDztE8JD|+8OM8to8kXaIEZ6P!oqEN_xOTvZ}`^=^SJTK#IfQ2Kd13XGBYFG zS}Y8Qce$;?y!-uL_+|Ki7Uo^+6=CjoJ6OGAd`om{Wc7~mY0c(iv7ts*HVl_>;2n?e z0Tg$@Mup!`bZTU^?KIJOm*cli+?IDjI`zEE9H-d1jAWYZ zcslQG#Ac7CJMV2oKdR{`HRioqh2w^;V&}b$Z0o$Y5q93&2oKlnLmCea2CwpI?qY2) z!-fz4)NL?qv*wL(I6Sn9IBhT_40<`H>zUpBkdM1|G%0IOx;&n%XKDMg%8b;Ok7DI@ z{mW->`bKrc(`+P$N<&*iA$O=>hi}lD^^qkX8offs0~<>3_H9{L(EhEog1eWtj%!vy zaCgRhOZMjX2dn`F`&9Dg4MzJ@QThP7VkMx$?&8YeL{9(WWWgjfZ=_n zb%ul0!%!z0*|2!Y&g$Xe>Vio1i0U4me2?87M`&AJ5qI`>?uTsv=IepI>4&rTVV00# zcDK&Ho-`xpc7N?K^L_3M^OdBU&R%!Bj45x&!uzr+uc&|h_QRg=oce~mf&+QOQWiDT z7PR%<{dVkF?eJr@!;aOC@S4YJ3l3%PNk0*A9{ahU^jy>HZ(Ew>Dk$)c^zX*pMy}e^ zp-|cccE0zT5nn;uh&ZY(V1y;zQ7 z;SGh3Iv?%m`>?cP>5qlZ3!RK|#L)*(gku=^t83dl@F*O5PgOd?C-ZTa;m=dLFe^I@e}$(1fBv}pzwxY#Gt%Yc%s9^t&SjB*;`fX$ zGeg;(|Ex-w-IpEX(ZD3=CcR#rtG z|G&_)Zysz|-0}Ys_>EY2UATTTeB~Hkjaq@vb=Iww<2s+Wl;isSSXk6By)!)RNwaQ2 zef*JDhCun`&>Z|1L$Flby0Gs2@mydyt8w0ZJt!G~#TlEWx?#?ou5n$|NG_jxXco@L zJ4+4d5-*ZXHvhq))HKKu91q(Hdcdc)N%$cA?ZQ8Y&qPEU*4g=NN#?h?EUL*zVZypS znZcHyRkk3A} zp+;8EhCdOVx2`?HymkGG%nS?xPSBps)^O_E zWol&gUGJ4*&)b;#UYWP62I>-ruZj)N+a1Cuolp@z*6F-h6!X+5=B-pQu#0VtbsOd- zWnZB2H5wOdJXPazjh$;Gabq4*VJ_3SU1MGsWwTl1$2IQIxKrap8avlW_Ipax&uYx; zrS`>ZsMx2m^L#4dFbyjk=lN80=lN84XmI$IKFwY1d2RRznabDqDc+g0*wmiax;rHk zG|ln)^Sbj|`mpdi=3vRgqQ+DQ!nvJayC2@9+L<@;5iH5f{vhUEIB!JA@FgvbPtc~` z$X0GL${NByh=sfowN;8)Zj_;q4Y;rGx+!*OU8~Z<)4~EmBzVhEEd_tnR%Djxd6+B z9ESD(gzUE5DO%U)PyBbmmCy7H+HhOrZU24-{g-+MWdbX~!eq2ed=`{Fz;r$=?g^xG z&vV>nAQnDv@wt(Xh0<_`P=KN|BCBUI+1#;F`=SjKKNC8``4SeEeH*bJ`g?8%I=a0dtBRkr5_{v27!E5KbhJs+Y{<%n6%0Ds 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 literal 0 HcmV?d00001 diff --git a/lib/libphy.a b/lib/libphy.a new file mode 100644 index 0000000000000000000000000000000000000000..5679e419cb110a2f1f96d313d90531f49b523c6b GIT binary patch literal 136808 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%0Ap_76frcn{vI=kVGo+cP*(Y&xxOEo9Qcq$; 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%0AhZ5J# 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 sdcQE<=Ja{*3vusryvy-k$NL?>AhUsTVFRR910 literal 0 HcmV?d00001 diff --git a/lib/libpp.a b/lib/libpp.a new file mode 100644 index 0000000000000000000000000000000000000000..6f7980d54195cec05f9f80cbfa6b1b65951ff451 GIT binary patch literal 165592 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 z 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 literal 0 HcmV?d00001 diff --git a/lib/libwpa.a b/lib/libwpa.a new file mode 100644 index 0000000000000000000000000000000000000000..efdb76f4517b147688b6cb4b332c505e14924020 GIT binary patch literal 124372 zcmeFa3w#yD-T%LPauO1^wtrz6{KA+w1gdu9N|JU=p z{?F4_N0)ieXTLK$J3Bi&J7;F&ogS<&srd51i#=z%w5e(7)6xT%UgGz7@IRdXug8;? zo<7xyQ8dsnj75eqp!e)8X|-W^TF<>*l4ltGx0#4LcjKqGuMIVfFS5OvX&9fenXc`| z7urUr8|FXSmUS8CKeIh#7$&zzr=Hn5x()LmZLb_R%(J)p${TA+gJo4YH6``S>l%$< zSwmr&!-bVKm1hZO?JOfSi7274th%mod7oWFSz}XO;nKQbVO_&bed^M(y26{vmKTTA(EIVVeq^!nHO=+d2g*TNt`)>(WRhIG!@z}E8DNzCdBwvc^Ij#y;&cYDsO~a%IO0BvH!i%lgv7 zRW1qk9p~1v`r5jnU7$>*4i~+#+|FS`bs;J$FA(Yv&jmvLva^bA3F>BI$nXODb@i(l0CzR#r9DqdxIs6gDm^DYg&el$BF>EcZ`~*k_AQ zMBXz<&Q^H$1?$hcTqTut6=n5E-npE}Q(8i@4ZUh?}aW^~JtahraR#Q-OO3Ro6ZYrF> znfg>+RgIRWtPwS*v8<%Aveqeh*t3NyMhj8fci%CQ%7(r+z%VZE|6q0=x}W=Rz6n;KhCWoh0<<{3ZSXMCac$zyH!p@r0jp>1CHXmPG@< zm6JEqG)+A29cLO}GtPtzBPAMogkemzcPAV1Mx0oN>4)*3QSDCb?D=s19&dqfy{jc6 zVsm1B#DJ|4#<8rJgoA0;yzGeB{SoGoh`36#`5Ch~BF1dD?{b}_{_}a&5iRGtZZ&>iqFkNb`W{S!D+b6!(jI+L!5 zYMa8u3nJ4a5NqBPrchVV*z?ZHW%{#mW!Fe2Bnc3BZtiTw@ z?XqCA<(ZZHy8RuG_+iz&dS$bnm(|%w6q&&OisIhYe%}ty2f;1ne&jSW)M#f7DziSt zk7Fkl#XBW|bLcCKqIpe4A2i0?T$kC@9NpA5!!8IEM_*Z>bXXjl2Ra2(5c$F2Tz8uh ziZ(K5tjsj*GRbVh*{^mXA4b@ZH2g+muG^0ypc#rF9hGRMv6`7jj*%yv8Oi|mIP>V6 zA>*8#vvtlD3R9orughhga^2X8Yl@=Fe8;~mG1q4o>XsRPr^HwcUA!VFv@XlP!0(h* zXokN?${{__R}4{K4{tNyofkgfGUtWs<~22MZYs)c^!GHPcJTV+%uxzf`>FthhM(uipN!uk< zXD4o`%x2Gy&D`9SIqz@zvt!z4M`z|{WzQbCIlwE6oZ#^OqfJFdBQ6_C;pm`f1GkqE z-Do`G?|mmenrHvP{G(YakAqbnPsd9$v~$3Z=0XB zXVzToNwXtl6?|R^+?*?BU$bCNAMCb2EHC zzO=%{V@r0U`oOOfU+HGDv;Rq#VYEQp1)q+#u&D->1M*H-%{ zSD}knAFMJa+x_gx>@ZEPFRKbpc6K+S3vNtCm9OTz!sKQ4{W7_d_1ZC8*2pBz1klT= zLq@)+8x2F=Fi{f0={uZ>E$3onLi$H`+($PF8yQa!+x1UR+4-@T7wlYim&2z2aa95H z04jf%XiU8Tt-(-490cZtryU!Jnb=ewfX0j1F_$1t?YkP`0ZN2_0TS7wb|S1Ws4>ld zi-H_M5U1=GBdqeuT~+9sjf2np#b8tUs{yMxK;zFyubUuF?W;ssPp=K>(T?e%2i2Dz zE4M0Rpz%0PYAHdSN{>G7sXq58Zcsl&R(#&8JQiXhh z*%NWr88dx7Ck*RHWLQ6QhV?7OFl*Ks7h&gG567N$M4lJx2>szmIuW5^U^XDB@EINt zUpd-MQ33c2Ux}><_AD>jUjtt?3JjMctQ_rcRsr}78`qZkjdKn#)!j0$w!U$W^QNw> z@fLOs2YWCqrH?x4j5i~D?1RH3)n(Oq88YmYX7ZKYcBVAQKSzl74c!i02J%+$bHXgy zZeiX}CN2$actvL6AM!%@&%h_Mp}tIXHo|P6O?fTJ5SuDB`5Et}(Eou;?Ckh(WlYwd`3Hl?Zl2cpc4Q#*&|%kJcWR_{yV=X0 zWQLX7xn}ajP5Wjfn8zuk#*K+E|3c~xtT?gTXRLX5;%gP1R-`px`HPDl8(m&`*Xqu$ zO%IK@ZvBg!?;3sZkqsN(O*JzHA;CY|@dsyE;6vo(*D}mGrumAk56Up1AF$cV7`%1R z0PFTG|KZ;{cYyg4#Wmg0?~Xn^;J(LlJCh$ve{r;L;$v4$nf79N@%NKoGxyTMa&^r= z5IlPG2P>nq5<4@|d;Vg*U;C>3yVon7SazoEZnEmc{%`E{vQeP38;4}apMh`-whHcm zi^fJ~d(@8YR%~p)$ZN386+Q@`oi6GT(f~2;|L~sY|Hgi&DqfZ@n>4!q{mw(^iZtUg z@X_>-`kj}-W~QBj^N(CPqkq5i8`$wh`kkq0U+v2-X{z-4_d6G(1IK3-^Y@SXotGfJ zB1wy$O0R#vvjU0Gj_LiQe&-L6-WTb2wxYkwgPp72*@|+W2UF!#{#5w0{QoKaPTuyf zVq?Ehx?Og5*|%W7Q#pn`DgdA1w0_}?eqp=UXaC5k_4Ncx@UzW7`H~gNY^ae{A229-1^jOcv#DbeD*d}er$$!k zvz(O8ePTn6tZatThLslXVQkDNHL~hg+Wk1m^OIsjjjYlc16IfSjo45lE1Q=^XS4lh zVK(O;Y$hu@+;MEogGV@4bOihQaXQ=O0DHRss{UpWzZ}`WNJ}U1Tz$Yx0+;Xy3%GOt zHc`O~j5c7Zo#SLY$_5$D^Gzd+-z2ia;lQR8E60ku`Qva)dx165y4<>GYw>woi@jTm z$8Iei_jKaZf%f7H))tRxDZa42ctUaU#6_+^@#sKtYIc6viuVG=7i(jf_95_(FH~xe zKpynQn9j)KVV5t|?F?e>b#2eso`@!TPu%nlUnp$t%np@W!&basIj8c5MXC97Mr7X* z6}Z7Sac|7wXfrjgE8&1M=$RU49%X+HqnUrU`*m?M|8D=jVpu#J4)qCTHN>eh13&GP zux&EY8gG!-9|+j$=jWiNqaAx{Ah}M zjn6!3XUWw!#L65cM4#9gias*YJY@KG#7BM{emUD%aRLLn(Pngyl{hipaz!U-hvxCT z*?4PCaJh_wX;-$TSKMFFzBgjY4=z6Js^1a6+|0dU_2Mhndy~&gA7j49BOc10-M!wN zefWp>y^ek^#+D^PD{=S7cHty;?mIR=#ye}}!K@F1d(u8}$L_pmjN6J^lrRzJ|B97v zj_xsf9`AY0+cUY%?+axNo_Mo)f=PGpPfkd`xz(3GcACpdToqv;_oM%aXJ_mSe|U5! zj+S;H>*)Lsf*h0f+qGb0VukCTmyOHriLesmdMgrV`(928Tduwuf?vS)cn_xWC$G2N zR=gG0btH4&ZI5o8W^Vj$@|f-uzV767UX;JtWf|WcSd)Uou9@!tat8+S_uaOz>J#TA zk7n^S_2Kg`(53IY$^PA^|5xhMvwfR~jSre`Y<%dwfsGCI2sG5x*TJWLRQyKQnT{mm zo5@Zmj)O@p*sjJ#r>9MuB4`V-(dH>^bhII}1JRD{d2DplRel(EAHsalF)s5*M;nz_ z+Vmnk0UK?G%R`1X>O5$Z41JW?WNS8LA9UuSROzrMpC|S!m5wl(WkUNqG`oT~yv%f$U{lQSv$$4IpdZD{#LLNjY-0E>$*Ut*&F*XQRZ2N^8RtTfyTINR zg|yh*D5v&a2Z7a2xjC@=toP7L7^pb3{RA1xB#2Y{N)gu2V;Sy^B4|7hz5}WAxCN}@ z0F4Zs$bRrN>(kS?_bla8lpu3!O=i$J7pE#N&O2=gVfB zuQp7N%qB@suLB3OZwu&jdfpx7)Bk?@Jyb7grhj%S|NQSE>S{a3{`Bu3`dcTDXW2(& zEY=0Sm$IoY!giB{Sr3(CIb^E5A{Mpic6U^5>7!2JOmZK#oBML!BwzeSxE z8){_rThwvdi2XpM!E~sRRXSt9s(hx14K=bl_b-XgE}93M$(t2!hUnDDv7(Qo4f~UD z*N6=@a;)e#iO%lY&BE-)-6G6x;&Nej^?oAEZW-HGo(uU`8m|MZ^W7ynHL^P2=S64S z-wCq|7#3#ti(NdGjz{VeHL^-)yy)!Gr3kaz$1wB8E*Cu@;laBYPVpbQ$BQ*AC zoUE~5<8+OIed$;l>uHYDbUmHvnx3n%eV+%Xxy#w z0gc&asj}+T_>{)c$gi^DHB@ZhM{+JHnx3vP?Xa`&ei_d$8ajSc?ISsJt7E6!bB#G^N~{lKWus?gW;SMD zVRTQ8oigF>x9zc}l=mh)9y5L2gsE=dj(u^x`Jti-PYx^&l~1@7!ozXBc>^Z=A||h5 zLOOzb;(Aw{KrDOgEhb^F%jYt7eH@->-m}*9S!T}1;d@+WwAq1oN}s38E5mCT#XA*7 zoSqnS*8F2>A7<@IIOyH$Ivnwdd*eE5P^yLA^UL;NUxaZ{w{MOy`km2-|NP@WVfYHe znMcz;$l@@iJ%EK#y5?^_a@%w9I6<)PI9@hCCTp)t4O*sJYtpP4E8hzTPlo582(QO{ z&bEiM+Mdk1-e{g@7Hw#0u8TJ+nzn4{XkMC7x4;wH(6J%Sh|X#@;)@K=Ji`;?HIH#{ zZFUM1TA#M^^>Ev=tmdWh%@4<8+HUimW@xpiZGBVQQcv?z&vLKx<9yeA{&KDlx9Pe3 zY)0OlsqK5bha>j7Frb;@o6t52gSUTW1_C=iOu9Q7p||a^zLQrbu0X(^KrlZ4YU}sc zC0;dtt$WNvOV*C{c(W#&qkZNZw0zsOF)`K37?jJoudQ2C2bn!~0)tjznyhKL-?}C{ z$CwruozUe?9dsb=sFUxBaftsi4Ku8_UAb}2)PZGjR@7Tdi?1|Ry%$ayJNh^h`z>vf zPJGni>xgZ+GtTU0c&{sI|A#naocS9D-?lc#SuwwPWbIYP%s0bdU^0Q+9{(P8{8t%v zgzXCkr(KHr^be=#;X+K~AUO+6DE z9ra9X%KmCF9d)uB8|^sEvmmw`%%-(Qw;;*D#y$zj2&Rl`o3huB-I> zkFzbqyWmDV?+0NY>m=(m>y#R21KO?w*J_3ez?bUtsd*f3q{Y6Oatec59PWDp2jU}G zIc2vP0`s8UP~3d%gEN1;zg7Nfz$y;VcosX@-&0OuB?LXa6r@KxrpMP{m0thxv-KEM zOR%5$D!sFl{#m07q_Lsv~j&WJ9lw){4Hnus8LwjB? z<+#651>iHhvR`;jzp(NDdC|7M{8}52(pGgk3q@ruw>Elp)@?~pl!o8YpBM&j~JJA z(@FB5Kof0M8WKJQf?m->Ced|us8=1mCq5H{NU z4E`^KH^D!IO^s2#CptB<8h`2mtNA6AEBkegj3s))j)X3_%H;YdD5bY%o z90%t)QX{K8FfH|t&OVJ|U1u>Vo%fMqws(sC8c)|aSL16nF4DM4<7FDN-B9VbX}nS6 z%^E+UafiliUsO5=G`8;xlm}feHf3{4<7k$l*s%N*do)hbI9=lz8uK}+;x5*>LgPk_ zdH*PTyG|lZXV)3w2Q-^48gJLQOXD-2;BdxI&NcTd;P#4ajvtKw>Y(>;tF03lAEB6!? zG!_*#+AGXG^X8qrZt8_8zqsxfo)!yZCGPU{;cG*uf0*bh_YIn3C0Ea05bz|JZ-zU# z@|2?jV)t4HvSx~UNLV2)7f$V zYi2?Gx4oAAuY5041cZk}i~;SDxnuHaY(={8`_g!aQhx83v!YvA;E=KJ8>jQuE*We#d7%9a+tP z_kyXj;h3m#ipFy^=KKR?|5c5H!fcF6HJ#UA%{Q+Tof=uqHwX6h4|HZdRQ-l8(pSTq zpR6}`76qQOPcZG0FZ`{VZf5#(`UK1~e+Nm6!EYJ-_QdXJ!|4`aS)F`?BF;6@Y;DK- zzXWH>Gp1-8U1?{&OQELw+Wm{qnUT%kP4Lm1J>FyZZNq;g>!I_b3diDyo%mQ^eU!2E?ugW=xZc?EC2M=_ zjGVitai*;Q-O>10>S*wun}?0ny#kwhhS>LFvh!?UJJf=03^qF2kbT(Nv3(gE+kWya zY;?5AvkCV!(joI0Oozv#qm4Q?ZTODGS47&BW22)@h4{3o19NHzZSKIvu@W9PgiUpd zeh#K%+$}aac_8!I%D8;~azEq#8XI-;Zftz+koRGuqaB&gbLzjx#&aPb#70M(e~*Q; z5Y9Mx{1|>nEFbm!cHXl-ZO1W3#=)cAcC=IFOMzuh*T2uzgT(ocdagcK9Bi(1lCb|( z`Rm{3x)wXWNS}*i=6p^mr_x&tLFJYE{HRdl;Ge6{H3sSN9b7qu{rg2t9?%fQAy7g@P@z;G3S&#l zelh+ugjHL`@D=^+a}ln?_7!a0zYzWwZ0v6_tY%R1+mzaHH0D?qRy%VwwQ+2HcpEI= z6Q%bLJ~8U_`PgsE#4*`kl2!Yy`iKlurz5LABI~Z|!;J^ih8kJ*;rybr?dQF%=Js7G zIyJJI+c%Fk@%RUB5jLhtjjZO7HH#jCUx7{8vz@0sHL|j=q7C1E;qJyp8){^ATs3aZ zcu$HA+xzXpd=7L9vt8dKybu06(46#;j*5 zog$6xbC*0U)AZXlZqs<9#+x;MLgNmNyEQ(b@f#ZVYJ5uLe|FrFpReT?qtwi(bM@o< zJ}vzP)^&0Yq~4Doz^BW(ySsf^`gd>lU#7P^xuNgVX3kg3v@e={=0F!}&KGj*`|?&q z&9WW)ExME2)Sl--K36}8&m*=m=dv3E81PR5U2KCh%o!N%H0b1tp1IjH`-3Z zy%SK8;H&ib(<(n?`$g~9DN`?*>U@mn++*>*={OKh+$qCkjE^K3*3j|30ps_($DhE= zwN!WCI$14i{}d*6hE4>BEPw4tFfr!c9l`kh!Gyi;!;!8?Jmq3vXMFl=V{SJOa)+hv$VuzKcs*v&}9$TyvnqkQ^#=9aLldi;?a@C)G%Z~T65!Vz!c3Ga~T zcN05{;(EuAag{HA&DwcU`L{@LuigG@W)Nhh!Y6M4B_(9HyoHVRokKAka ztrK^S_}Ty7fOu^vXjgEUZ)a|PaAZn^vo`vk6Oky%fOn`>8rWH2zH66JXOeSpU+4MO zj6i3pGquv!=|?PkV&y$+U3Mtz^>BV?{Fw3e-jONMcbfIjjdQN~J!?@4F7L3!&N@zt zU4 zl?l#qF;#P`J3Ya-qX5gu?W3lfvs3b(btau6C-1PRTGxW?iKjCMAbVDX<-X%Yc%pSE z(U&sR`M3ZK{zOyDO0}ZBU1xtF;08JNCaheK@h(bmz9?W1nOVEtc!gQxQ>)mc6Jo4r zBWBE}QrU*G>AE%>HTv1}GD8yQiDacVD*gHD`?Qtj{U1Y-7x0Y5bLwsT|qiw+y7> zYkgbNvA?Qo;k$YeIFk&=*w*~Dj^?Y*oyGJs@e=F#zwwq zp2P8)_jmWX_;v8m{$S#f;E)r+p*w;Ju3-G$h@^a02_q`$clZKZ@pX@1cfuVNa0gxP z$mS^t&3J%>JuY`~_OAQ;UrvH7&CD&f($whnrB$zLWS_1sQh=+Q|jW-HTQ zc;apE9bs$894kRB=i|(#wCbZnuW`HZ=vRiV&SAVt$Ke z(RXF<{V3^;V+S@_tBPvle^R@|u6e^PchQx%MZOY#DA5%a3eWTz)|{x#;a}8dSQ$|d zg`@Mv*CmW^9@H}E+2&R5qz~R)+wuOw4R3B9(>`fx(yejHo7PO-m&h5t~agJs2_x_+b$bC0GH!8;oSp`jhS;ylzj$1LsDWcV#d0C z={Fx5>zxov%(=GEUDaFSwuW27 za^GDWdKJ0E&dr_eHGW)^w$<^OId}JNaBtZF_O#4>cKPw_s`$!NDQ+{31QI$SCr787=N9e!0Ngy`aFXNI*}dA}uiavhuNmS|(4k z;>RCwkMF{(e77GT$VMxj7?^xX>;2i2FEuAIX3~+@SV%c7;Z?a|WX`T!6Cb;G%C299 zU7Jz{THT*acOQ5x)yRl5FJJ_8O%6O}MND_6;_u&3U|~dV5AS{^-2Gjth`XK&XIQRo zfBx^x?8pJdkx}?MdC?3H{w>i zJ~+Y^92LOhJvcPOjYm2j8}^4}0^THFXA+7`9_^Q^NBfuYnML~n}!BAK2Tg9vp;7B zR=21JE?@fLR(~%X#{W*>|EGTwJDbs$jd|A>JU>5pUU6{D@|8}H7M6FJ<;AL!k%|L<2}&~twMY}?$e49Kc zHVqBz9Ty0W`skBMsRQF&!I8e;XrFgxMN9FH2S^wP0`rxSi;OHek_)%(lr8cyp#yvj&WuI1L=3Anq#Eh!)-Ul9SCi|Ji}@CP`cis z$m+yR_`3(NO*K;@vNst8n~IUKJX@hfc3v?{H7{u9Qu}tz*!9K_prMb2_cpWPIW}nrM~c9cvoH2VT2rGTH!k z?&zw74w!bmfP8gg+Fef{mn z7ix}vcTz%JZ?V^xH1h?YcLXlAk5(su%^st}Y}*i2wDN%`;KxbgP!2b+WGN4?5c zg#9Gl;hR)vzAt;&{k98To!1AXznJ`5Zl`6$q|GuXdZ#6%4$8o(Mq=sj_T+B7=QSH~ z@%ROQ=G~bW-d&zJATT*0KUCU%#fkfm;x0R}>15{L?rv?vj&I}JQc1;C<*9=M_a8Nn zFdK(mz|_IcTCr;DhCvywYzT3gQ(CePhRy$^jsB5X=W|wimTgA9_uM>jeoXM#T?bdb z9dX$03Kd`J_u_lE5ypy7uq=&r%Kmm1K3jJ4ft5$^iVoV}#v0@HMMUk`Ie%gnzIkgo zJ#@Zzz}#-GZ-|>SD0zN*R<-5#%gflp9Usva7OydP3-^C$#jW~N_^dSrd2S1Dh_Xfy zeP^fBVt0-$_nO64R5IUf223;YU_X629j{s5-f(u+^f{KiruJLcC+{OMrdrKtf6MoL zycIq*Hs6YW{|eXpW0${QedSYgX5@!XUcWdH*?igS<8UHL*6RAu-|HIUv1qV9r@68! z@>OGe;x$&ZHF87iomEkhMte$P>u=J>Y?~7M%+piw9^tCGK4xQ{8#%@+NY$4nrQi+6 z9$grJgWFy`iJD}8447SVc|gUi*nJ|5FOhwuo=o|nC(y+|=slLkEL-t=UDhC9=yYpC zdfxSix`*JQe*K~BxDL;ZcmplsoQ{I3V|lGj#Zb!Qi|2IIrz9S!j&EJEe$47=v8(T# zwl?+BBUOpzBV9+Tu1mFCRbNKkJGR}In*LdH7;)emTrs8<-#R5Zdwk*$zU<@#+|9+I z(?wI_jVYeMjOOIXHJ8NT>w1+B?)vbwoP;%@bxL~n__4!Sg8A8zc7z%6R+8OlH!shI zdA+;@;TX=J@o1eAoju+^ED$<*`E*}Ib9U%-rZ3(an(6cSu4r~jamutjyAbW_7me|x z?5Z!#YVGR#yqujDKgf63Z^WT}))33(tC||9n!-M5d#gEsojcy_rIeNJB1O!vMQw%p12R+-N?aZz4!qEqWz_h-0LQ1e>R z!9UJ>%Zi9L(Q!XhR+}=NqghS(684@KV>K>%U*`d|=wsu6a z6=P0InC8WWdnDZTVsxkdfoti;8Q;J-MNdptYD8c{8T!ocxyQebK66UM^q9Qp;^(Rg z2DDEo&EHjk1~;MoH>a~sge%R^op-NUa;LZcxjbjc1TVf>iE8O@|90Fdr8EE5hkk9X zwg(}H;zibZi^WSVzx0QvvW10BZjv&-l5secjg^D zUhHaq>v$dR(ClUui!(H}ajmx}oSZ)=4p%Z`Fs`KCRvgA8rKlL=FxFdV#LY1y-wmgl zft0Uir!3BSFq{;KVsv}e@51blEn|BR-hQnJzQ&cOTjq-_Q(D+-elevfE_D#PF!m?I zGfd~EI&Xhf0&2+~n(nXq(r3LUTk%oH*dAH;upTcN(pQh`L#OL-Uk|TiXRNt08y`a7 zifYVp##D5~%|}0fumHn4o6-N>9~^liIC@8L#F5}AS8({zL|5n}Ya-g4webVl`Z|qG zCK{U@6y37z=$8Zhpuz2n$5vhbi)43%tu?kl#&ykq5D0<$`j99A3wZuMD}3w zq6hx)Nc`5p#`K8G?v4KF}C7lki6d?c7q?#sdhsV3k_+;Sx0tv8OmkehpLc5uwb zD=@^H;dVxI@7ZtpFtFO`NiOyUldK`xINGrM;E?R#P`uD!=9o38K6Da~fEa6FdvNI5 zU{XtP%&jY|==uT8Ex}=RP44>OkW_2eJK+<-#I4>julRnsbFN|Px&&sH^-BjP%}%NwrApeqCGsDJ0B10sPDSgEUpiA-@0N^RsGM>FY0_D+_iW|%K6(;TzMn*J>z*gh20eCq~Lduk!PQ!w6oF0sC6B9AHFl(*KQ}b zx3;hcbI13?Q6GiVSLC*~-o|j`T8^r>-fr`{aMXL@f{m?rFtqF5@T$Lti&F~lq-Rj0 zXYBngf3=hTVR+Ru&i?O(qn@^D*Dc{yPuX+_;ye*f-~P}wQ|-jQ5uUjvoO^s@X?{xl zp=0i!7j@o;F z6^{B5PH5w3oKJU0@+s#sH5>fN>r5|*r<~#kMg6^2bqZcgderSPfmEXX#(Hc~q#9r31Lu{iW&`Q~Z?aQp_ z>iP+ZTZ{y}?{?(2{++)l-Qe`v?^zq|>h!dvl(^75C4a>pXiUFh(pY?Y*RJbtyOQdo zioFSGzYFJ9Tao!$S(%wbv$N1#pv_ozgVWsGtpke6G?H(4+ZgDxhSaBgIsg8S?E9O0 z)^-$!Hs4sCY}Mu5w!y@($~OkBuAb(-Ga+3y{C#5>I8 zO|%C~(f$v!lJNw{A3t^o%K66P?5Jkv3BbX~nay|RTT%7C#L6SBOExF2zdUxshRchK z6DxP*7cYwIMci~d?v&4ri+FeDpNY4q9Sv%6BCd~ie;(TB^Id&W=QOCo{hmy8_WC#Bko_W-+c993e zyWb2aYt2atM!0)o()Qq69(yBvq0N)VPD}{w95>A?f7;HA&Z0f%w;;jSox5yt+PmRg zv!V*UpDpDHH&zwz&b_1ibU|8I){6Hn!xMEsIwcsKON@!$UE*bJ6VtP;C$1ba0A0D_MEq8jzu(M{ zY@BypR`;8$6PKl1NqDrT!yCiVfm0gmM z+>l-}v1-~1_N`;`?)YoCV7e968d*7g4tm*XC&GBe(B5!RUYsvOhtisswI>)ef3GV! z+x(cdZvKbiIj&_{saE>%iY4o>+p;QQgZZ7M_0Q$zcusGg-xXYePL3y5N@gJY8q8CS ziM&`IS4qOIkMIL!fw`g=`SwIT?i8PG5k0oGc^8^u=Zsuq6S|voZ)=@_@8Hb*C|vG| zDY&9FXV<&osK1BH;}2F$S$0MD%*c0XxiNlpLR14nf`kCeaq`7&(Ohapp($V%o17U^h1M)BJEY%osQ<`T_@93^_C#c5L^7aS3hS3dUu& znIC54Yb0&dOG-4-A@h1O9oAJk9!u3*9*c|TS3si;Kk`OL8#1b=o#uBnUDb8Q4MFFm z_7-ffX!fsa_GDBeJDpQt&S!1M7K3|(j>l5>4bx19J_}k4wwpB@GJi8?-0y>tl+Aa6 z)#41^cXUis-GfZ?0q7S%W12fP8!~mKc|_CIeapDL&{Z8y<%NS|`eYw`#=Tn8$*Kz!i-Diy~DUa)@(xHYoXER zB=|M~^%aKJ`>hC$r6`o}uYvmCsyFC#!3*K-0-7lFmx-O~S`D8?tJb zKGt-yx|c_?afah=GTSTW?GjBVtF|r|%-@k{ldsv3Cy2gW)5)sMzf04}>iN|UR_F49 zW}}|VW-^}T(7C9!1zVx~ki|T!!BER7kNJz~$k5SjN8?T>-g^CApp+C1(6 zKBV9nR()$8%Yv?sH3qEW2Egi>yFiOuAk4VrG^EGl-U;Sy%VV+6PRC=Z{yXj0Ls!Sz z3|7bb71#$`#_iGKl3C@b|5ej_g_$N*Ga)V_fn`_|(4yR{7*R z4IORN`wi32gPsSC>GOSujyCGuhc?@xt84MLW<&PCmdA=iqcmQ43L7XmrbBkYr_Ckc zdBS&qRexq9n90&+3z&{^$?AT2Qq#8yGjC*djXJ@qeEtAt8cg$5v1hnPm~qMMp0!|m zL)kzlE1mCtbd0Os0~yzi1C4{mxXEDL4)(F8f%&}Sai0L=f17_pfMYu3rSNH!g+fs8 zXt#oueH#l0j`m~^d>(g;*zmZ!!K&O|0ndQOG|y*)568G<^<20DtnQ<^VAXD1ui2A5 zh{tqp)pYgF%RIC~@2itwRmXm!*^^b>ep1uPyf2vkE=?z^{Jf~?WR=dVnoedOn9jSJ zPFCrwA7oe#WL~dTCdvcM%i4nNa^5U(v>~(mL4Ag%lhwU^t)`Pzzokag$zDn4`2J_pJP20pz*Cyd zHenu@%zLl}+fJ}L?jOJ^|8Id+Ih+L3@mWk(al>F0cL+{ImA?16i1c-+S|oviY|L(|DBeU7ow z@m$n+8_(qc^hMB^W*R$4aI{flakNQ?eknBC)Mz&R;en1eWEM4T)@U|l>h0Jb0;~4( zG0jGe_c5Jq(EXCmQ83#X#{ERISL1@Tk48aVA@;LbXmHWk@`PzaR?iwPBBEn`Agg;} z1(>h5Jl1W>Mp%{YT1_XbYw=@EC#!q&XPQn{_2Czqt`=n2=MJ6kGAzSa!56`2eg=<7^k6eUR6#(|-DN|$WjmY2asZN)Gw}Hyvd@$2l^!)8p;0(qr9F*9>SZNH>fP1aT@o z`l^h%D}Fl87ZLeLQ~BegnoWqFUdLAqLk*K-S6}|fpXVaZN`dL=CCxC8a>hm24-q}A za_wjlXXTvn+4!7s5q)=`qea*c0ewI3gflL}K4U#R_^k6{IdHg0IfnUIQI275%8~Ut z%kVh06F$SL-cn{bgCLG!-bczYoUa1#87}M>u0&Y=odcL zFMP6Jc%XK3@pwb}g~#*@Ux=_8BIEf?MYtIeA4FV+XZEvS*e_hvFI?L%{Plie=T87O z-c(sO z>nrU1@J;cpW%af8mks%mfx`0I`ogkcNrnBr!?LQ{+B&4Zq_P2%6ABwEtIKMe8vA_- zhcgTKfyA1|I@9)z?C6|yNwB7* ztg7#nY2bX={w9=sWm$tA1sxQKWrg((HPDPgJKGIqHKi!Ry4tG#U)irOnOfMuYsu7X z3%eAm@mC(}ikr#{ov#teSBBHl3Q^W|O^t=zhqS9}OB*Vu7{RiJ!lm`WIw+-;w@`61 zZ`Xjj#+&S*eJLBN3(IaPt7+`}W0t&1bqzNe%x+!vO_im#EvT@uwy-a1X=x$O(cUGO z3VVZ9OL$+hdJe@s#Agp%bH(g~C}yA3M4LdzF1#sqny(j1Z^m$f$wc5d1fp!#&Oz9Lp6#wqS02&;WTB>;Ww2&hYXeO!TqW1ISjA38Sg;KW(!V6>5pp6pQcrs{7GEd z@X4gUm&9{d-%jUgtMBjw`_jJ!JQU}`_Ptg3F8J-jbCLFc3bT!NBTRd`DZ)(K{#%;R zIdvdMbRM761ZdBERSD<9=ifl2&b*hvC-a=XBis!CkT8D^uR%LY8~&u-A^ZpUlW=hA zi{a;KyhP(Sg!z;0WRxN8FNbgcTnP?=gLptwe*j!7d>{O~G~Ow^88$ya7lih_CMSjY z^WKNTjB6rH8}cM!{``5l#yOfzzA)3N7UuOoCd{}WXq<$H9n&8TKOlS={Huj&e~a+9 z;eSt<=|8X8`0*TLI+wtIM3_Ity&(Jt_?Sg#+ib(VF&rE1`O{RF#;*yp{*J;v+T0F4 zPk1%>4&i*Hd7m(k`+_jjoQe)9<7U9G6XyN#q;MDfF}QbV^Gop8gn2*RB^-~A>`TJ@ z3Fkb7X;1bE(`L5tV)%#A8K%vj;OF5!CG&p$tuTk_FTlM_eH{E-g?WF!C43tG2=uFI z!#2P#TmXNuFx!c(!q>xpF3GWf2YxEr6UODx^Jug`gxTIq6=pkho$w9tx!K2s zzE$`~@ONnZg2sn5en;c?gzfeZV`EH{>9~bC>^MN!9-0*9@MWAZhdASfISe{Pm_wyW z!W>Q=Da;|)F~S^{y+D{lw?1L!;WCZ0G@h^VO&arf%s<=pR!zT0<3}}qO5+ZVf1~k1 zjsK>x8)F-)tcGap(|DT3xfio0I)2jSz=8z$~)x{cVFCN;A9 znyY{r3=&F{gqp+;6V$(qfd#D*GK z*`&~hCkS^M8`GgiR)1rK-!!Gp>3x3a<}myNH&}FPWcBxury^{ky`dW+Hq^+mqNgKl zqAj5tFE-T3v7)Dl&gp~PmVbvOEbkaX-;rfOw2cjT|dF-=U~;O5X>xmpU*- zbZTUET~5&k_a)tQv7tte6`dVZmCjXSLyfG`annZfut036kyYNlDmwEPjm>0*gliI= z8aY;UzOSis;Z#Hu_cYxW(W#MRMYsP}Gs2vnxDK0%GNbDNGaYK=Skb#h=d{S*3UdnM zdTgrhy(Bs{vZ{M;iq3xBMr^9@`WMlukz=v3ZhR=ZJ^YP6pE{Rx=?8LJBw3wHrs(V| z#$)R%f6=L3g-wxl|zN-)X1tF zmWm$2`K=Jh@eE;8WyNs~ zUUzC_RSqAD&S{#bHRihr?WtSBocj3&Y|ODv`HT>q8aY<x^ZGdjm$d0`}eqJ zlO;CP$jatQ(K)qrkuc+OT!?w&RM9qJP6Oo>QB}_EqEjQQGF+kA{8VhHk(EueX7jk% zP$MgwJ)-j+CZI% zap7fX&z}T^*NgV- zT)}M;y#pFKR`j2V&S|nwXmOuqTj7%;~}tgd0&O1Defc!n_ul!kk)tl`y9%UoFi0nqlUF z;}Bgu_lZuq1x2Sujum~0W`C0~#~HdAH`$5XC^|K=D)VoOemnBKkM`1cyuX&wu%ikvg*73Ky;SX0ou#8yH9j#WOeNhX*T~UHq^+tnq<-(i-ew4?Z z4n^O+~i`tvnm-tXnYoF;ypX-aw4iB65I%Bq((W1Zu!6dP(}_1s@0 zI@4#{#Qd}E`aic5qW&T+E`g}1=}i7=p+;6V ze-oWk>etg=>c-b(%#a#cmH7(MIqiNU?NgjQ+$K6Ta;)efEzP^dh8kI=`MBtuzW;z` zj}Oh*=T42R>}kg`;S~SPn*H;lQzI+;-;2(v|E~yh>i?r!+&4t0Mpki8iO#W8#^G_v zTNqc~zanMqlp0xG-#F2^oZt!COWT+rIyJIt8@JI$%Krkfp+;8unIJlsB}^CQ@_-o{ zZ`abfMs#Xql}-n3Qk-j7BsSE@>e@Z4rBfv~)W|BGF3o0z*ia)Yn{LgfRcxq{mCZid zq&oTek=Rfp$BO&W@AtTWWe>U@uCHi=?GjjU{rYc?rjLyfF#dTGM}BDhSk zp+=4s{UmLq+;YW+8d;rtx#(P$^8xMCoHT1hr$&wy{X3$w|NLEHF7G+TxB(~blcG~2 z$BO=p=v+?2$rLJY9imestGu~2n=Y}TMpia|7oAI7;;{|A$jQTaGRi*<{>8#v62spo zs9y#DD;j5Od;n>hQ=D|J7M&V7R`f%(kuq5aJuZs;eva)HTjXcNyDmK)}>N&oSHj|xmbj3SmLXE8Ie6;9X0<@m? z;~o11(W#Nud&h9iejHfkAxm^>WR-{OM4ygilfkObSuQ#?vg&hwS9C57x<{D54c#le z3I30Sxx9yAUTfMspz+UzxqN81FrV*xgguD+iZGWIak-K@myblJMpoy-`L;Y3mm5Wr zIWP;if^nhK4HO${WYzz?LUb-4nx%1(FqabDEX<`uD}}j~sFf_o;u0nG_h#0K4K;Es z?pM0UMCY=iCxyAJ=w)H{A&&@iDbb&Vxs+%a#^ZU8TqZP9n9GE?oJf_=S45{qR^?M6 zI+qD86XrPNw}d%Gc$F}x+inu(GNA{BxlCxcFqa;2X_4yVy&yU@vg+f#Bzh10SA=8A`>*bL4WMG+{0W8iUQe)G5QoqEjQsie4-_mkfEZse8FvbZTUEpZ-vE zE*ZL4m`jFs3Ug^tw=kCsy`=HS!V%CS2=TTzd2X?WGLIi%yNK%678o zT%PnLVJ?fBDa>($S;AbBw3%s2oxfUiYGjp%1)_6V6o1oYKDo^4QN~Sn&b>l(YGlkyU-zrrF#rHq^+<=9i*(!vB>pmr;4~Zq592 znbU>BT;`M`JR5$lFqbeLM;vv}&J&#)S>3Zi(Ock`Yh0u8EgIh;%%xKsg}J<{m&cNK zvHL}*MppOf!=f{9y~14jb&_!rrgLE*&ygBgjT;OUopC>)y_9pJ=+wxn3`dF1WnHIe zKg~&hn&{NXv7%ogI+vW)D#pFT^+2i*(IyP7J@rIlY6 z=90oIg}HQUuJAV0jfKKo%C%VIpvD!#O!IaePqlk1M5jhp?Ou!MvvI6Dg_-BOg;_qU znU0kIM$xH}Rr$Az&U1WNn9E-u7iRhVhcK_jbHXg=-wJbS*~`K_?jMD@6zz45|E%$G zjZX-3dE`gJT$aYRfo0A#V}zN{cwwIJ2u;60nAc*0Fqf;PXzbVcQepc#3N!s2VWxkz zF!S&=VJ=-O7Ut5mTZOrF?P*~yOZyLDE=xNkd<6ch!d!CpXJIZ^Ys046-uFePMpkVv z$FW!@T++6V_SonWMW;qq?dJ&5d8~25T=q6mm~H8Lrh_u03y4mQtlHAcMQ1u2X+O=e zzfN>&WYsnXMQ7W%PBGfX|0B$FLQDty>3%FaHL_|aw~Nl@b{)c8Zr3Bs<#rzkb6MRf zVJ@p1fsJLwWp$ScbD7+9jWdP0G%j11OW<;axr}YLFz4#z3v(&sVvXyBxvZ{9nD$@S zxJ8)D7h8q7tnMz2f1vUI(KsZ`C5Vi}^WxIG|0~Spg-;6exZ8z!tlh%Q^FFY84!$co zHL`jRzC;@t3;I}WsFBrJ&?t;`F%SGi2$$=rdOJ^aYGhS!i$!ODzDalm{N=)2p4TkA z2L3utzh9W|Vuyv9&Kts9s`pni?p-=J?9_8}7?}B_MpnfIRT;|7pJQkPX{ZQi#!dza+eYD~HtA~WSJa4lw(_vWIJSNPzJB7K# zZlA_nu19;$(RyE)%e+r%x+!zFQc!LKgt>%oh%nO}Ae%W5gLypqi)dou_t*$Xrgv!Et+8GvZ)mz+4yQET{@b*2jziJkQRn5+m~-2dp04o> zjpu2+SYxg)QgPW=R?PWoid!{auQBJvDVr@CZ`Zg>pcb&!|jUUyR^V5|5vl{Qy_>jg&H9o1aq0J-W zyfKxgJ--WfboSgTVZUZGUE^GhuhqCn<0_4pX?(lJZ5rG2wdA;)HJ$UdR6aX2?$-E# z#`c^mNvBuSPiY*D_FAPmRAZ0EDH^A1JVWDo8gmVXO0z=aMvd*cQj)h;O<%9^0~&A9 zc)P}38o#7*kH*I}{y<|lf18#(#A`f8<7ADeYMiMtf45fY6lh$kah=91G+wRoI*mgb zKdLe3<)~vltMNXK?Kuo`Ui^(;*`L(d!0+fvw=^E1@&9A*eW0r<&i(JRPfkJtJCKBk zSfieEz!>BNlYkN|vB?oa5U@##5ELW{5Rw`ogeakx-X`IXSZRNNsMt$;Nv_vo%k2e9 zEw`m_6GVg>TBOv1f(1$yR4gJYC~|(^@0@4CBYLm)UF*K@T3wyB=FDe*_p@iu-h1}! zIWx0o_g9>yc(~#s#p4y1E3Q@CsCcpB6^hp?=J+j}&Mk`D6?ZB=p!m3Aj=Qq)Tu>a3 zu?|*GRqR*HaZuJKSMdnN#fm2@u2$TjxJfa`DcShfD1K7$M#b9{bKH^LcE92yir-Rv zPBF)9*=>^)rzy@*JVbH6;<1X$6i-)Nr+A^_Wr|lTUZ;42;;o8zDBi31km3`H¬C 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ыj(7d5r^fXj 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 literal 0 HcmV?d00001 diff --git a/examples/myfile.lua b/lua examples/myfile.lua similarity index 100% rename from examples/myfile.lua rename to lua examples/myfile.lua diff --git a/examples/onewire-ds18b20.lua b/lua examples/onewire-ds18b20.lua similarity index 100% rename from examples/onewire-ds18b20.lua rename to lua examples/onewire-ds18b20.lua diff --git a/lua examples/telnet.lua b/lua examples/telnet.lua new file mode 100644 index 00000000..18645187 --- /dev/null +++ b/lua examples/telnet.lua @@ -0,0 +1,39 @@ +print("====Wicon, a LUA console over wifi.==========") +print("Author: openthings@163.com. copyright&GPL V2.") +print("Last modified 2014-11-19. V0.2") +print("Wicon Server starting ......") + +function connected(conn) + print("Wifi console connected.") + function s_output(str) + if (conn~=nil) then + conn:send(str) + end + end + node.output(s_output,0) + conn:on("receive", function(conn, pl) + node.input(pl) + end) + conn:on("disconnection",function(conn) + node.output(nil) + end) + print("Welcome to NodeMcu world.") +end + +function startServer() + print("Wifi AP connected. Wicon IP:") + print(wifi.sta.getip()) + sv=net.createServer(net.TCP, 180) + sv:listen(2323, connected) + print("Telnet Server running at :2323") + print("===Now, logon and input LUA.====") +end + +tmr.alarm(1000, 1, function() + if wifi.sta.getip()=="0.0.0.0" then + print("Connect AP, Waiting...") + else + startServer() + tmr.stop() + end +end) diff --git a/lua examples/telnet2.lua b/lua examples/telnet2.lua new file mode 100644 index 00000000..4d614c49 --- /dev/null +++ b/lua examples/telnet2.lua @@ -0,0 +1,17 @@ + -- a simple telnet server + s=net.createServer(net.TCP,180) + s:listen(2323,function(c) + function s_output(str) + if(c~=nil) + then c:send(str) + end + end + node.output(s_output, 0) -- re-direct output to function s_ouput. + c:on("receive",function(c,l) + node.input(l) -- works like pcall(loadstring(l)) but support multiple separate line + end) + c:on("disconnection",function(c) + node.output(nil) -- un-regist the redirect output function, output goes to serial + end) + print("Welcome to NodeMcu world.") + end) \ No newline at end of file diff --git a/modules/bmp085.lua b/lua modules/bmp085.lua similarity index 96% rename from modules/bmp085.lua rename to lua modules/bmp085.lua index e9041efe..6ec0e5c8 100644 --- a/modules/bmp085.lua +++ b/lua modules/bmp085.lua @@ -1,158 +1,158 @@ --------------------------------------------------------------------------------- --- BMP085 I2C module for NODEMCU --- NODEMCU TEAM --- LICENCE: http://opensource.org/licenses/MIT --- Christee --------------------------------------------------------------------------------- - local moduleName = ... - local M = {} - _G[moduleName] = M - - --default value for i2c communication - local id=0 - - --default oversampling setting - local oss = 0 - - --CO: calibration coefficients table. - local CO = {} - - -- read reg for 1 byte - local function read_reg(dev_addr, reg_addr) - i2c.start(id) - i2c.address(id, dev_addr ,i2c.TRANSMITTER) - i2c.write(id,reg_addr) - i2c.stop(id) - i2c.start(id) - i2c.address(id, dev_addr,i2c.RECEIVER) - local c=i2c.read(id,1) - i2c.stop(id) - return c - end - - --write reg for 1 byte - local function write_reg(dev_addr, reg_addr, reg_val) - i2c.start(id) - i2c.address(id, dev_addr, i2c.TRANSMITTER) - i2c.write(id, reg_addr) - i2c.write(id, reg_val) - i2c.stop(id) - end - - --get signed or unsigned 16 - --parameters: - --reg_addr: start address of short - --signed: if true, return signed16 - local function getShort(reg_addr, signed) - local tH = string.byte(read_reg(0x77, reg_addr)) - local tL = string.byte(read_reg(0x77, (reg_addr + 1))) - local temp = tH*256 + tL - if (temp > 32767) and (signed == true) then - temp = temp - 65536 - end - return temp - end - - -- initialize i2c - --parameters: - --d: sda - --l: scl - function M.init(d, l) - if (d ~= nil) and (l ~= nil) and (d >= 0) and (d <= 11) and (l >= 0) and ( l <= 11) and (d ~= l) then - sda = d - scl = l - else - print("iic config failed!") return nil - end - print("init done") - i2c.setup(id, sda, scl, i2c.SLOW) - --get calibration coefficients. - CO.AC1 = getShort(0xAA, true) - CO.AC2 = getShort(0xAC, true) - CO.AC3 = getShort(0xAE, true) - CO.AC4 = getShort(0xB0) - CO.AC5 = getShort(0xB2) - CO.AC6 = getShort(0xB4) - CO.B1 = getShort(0xB6, true) - CO.B2 = getShort(0xB8, true) - CO.MB = getShort(0xBA, true) - CO.MC = getShort(0xBC, true) - CO.MD = getShort(0xBE, true) - end - - --get temperature from bmp085 - --parameters: - --num_10x: bool value, if true, return number of 0.1 centi-degree - -- default value is false, which return a string , eg: 16.7 - function M.getUT(num_10x) - write_reg(0x77, 0xF4, 0x2E); - tmr.delay(10000); - local temp = getShort(0xF6) - local X1 = (temp - CO.AC6) * CO.AC5 / 32768 - local X2 = CO.MC * 2048/(X1 + CO.MD) - local r = (X2 + X1 + 8)/16 - if(num_10x == true) then - return r - else - return ((r/10).."."..(r%10)) - end - end - - --get raw data of pressure from bmp085 - --parameters: - --oss: over sampling setting, which is 0,1,2,3. Default value is 0 - function M.getUP_raw(oss) - local os = 0 - if ((oss == 0) or (oss == 1) or (oss == 2) or (oss == 3)) and (oss ~= nil) then - os = oss - end - local ov = os * 64 - write_reg(0x77, 0xF4, (0x34 + ov)); - tmr.delay(30000); - --delay 30ms, according to bmp085 document, wait time are: - -- 4.5ms 7.5ms 13.5ms 25.5ms respectively according to oss 0,1,2,3 - local MSB = string.byte(read_reg(0x77, 0xF6)) - local LSB = string.byte(read_reg(0x77, 0xF7)) - local XLSB = string.byte(read_reg(0x77, 0xF8)) - local up_raw = (MSB*65536 + LSB *256 + XLSB)/2^(8 - os) - return up_raw - end - - --get calibrated data of pressure from bmp085 - --parameters: - --oss: over sampling setting, which is 0,1,2,3. Default value is 0 - function M.getUP(oss) - local os = 0 - if ((oss == 0) or (oss == 1) or (oss == 2) or (oss == 3)) and (oss ~= nil) then - os = oss - end - local raw = M.getUP_raw(os) - local B5 = M.getUT(true) * 16 - 8; - local B6 = B5 - 4000 - local X1 = CO.B2 * (B6 * B6 /4096)/2048 - local X2 = CO.AC2 * B6 / 2048 - local X3 = X1 + X2 - local B3 = ((CO.AC1*4 + X3)*2^os + 2)/4 - X1 = CO.AC3 * B6 /8192 - X2 = (CO.B1 * (B6 * B6 / 4096))/65536 - X3 = (X1 + X2 + 2)/4 - local B4 = CO.AC4 * (X3 + 32768) / 32768 - local B7 = (raw -B3) * (50000/2^os) - local p = B7/B4 * 2 - X1 = (p/256)^2 - X1 = (X1 *3038)/65536 - X2 = (-7357 *p)/65536 - p = p +(X1 + X2 + 3791)/16 - return p - end - - --get estimated data of altitude from bmp085 - --parameters: - --oss: over sampling setting, which is 0,1,2,3. Default value is 0 - function M.getAL(oss) - --Altitudi can be calculated by pressure refer to sea level pressure, which is 101325 - --pressure changes 100pa corresponds to 8.43m at sea level - return (M.getUP(oss) - 101325)*843/10000 - end - +-------------------------------------------------------------------------------- +-- BMP085 I2C module for NODEMCU +-- NODEMCU TEAM +-- LICENCE: http://opensource.org/licenses/MIT +-- Christee +-------------------------------------------------------------------------------- + local moduleName = ... + local M = {} + _G[moduleName] = M + + --default value for i2c communication + local id=0 + + --default oversampling setting + local oss = 0 + + --CO: calibration coefficients table. + local CO = {} + + -- read reg for 1 byte + local function read_reg(dev_addr, reg_addr) + i2c.start(id) + i2c.address(id, dev_addr ,i2c.TRANSMITTER) + i2c.write(id,reg_addr) + i2c.stop(id) + i2c.start(id) + i2c.address(id, dev_addr,i2c.RECEIVER) + local c=i2c.read(id,1) + i2c.stop(id) + return c + end + + --write reg for 1 byte + local function write_reg(dev_addr, reg_addr, reg_val) + i2c.start(id) + i2c.address(id, dev_addr, i2c.TRANSMITTER) + i2c.write(id, reg_addr) + i2c.write(id, reg_val) + i2c.stop(id) + end + + --get signed or unsigned 16 + --parameters: + --reg_addr: start address of short + --signed: if true, return signed16 + local function getShort(reg_addr, signed) + local tH = string.byte(read_reg(0x77, reg_addr)) + local tL = string.byte(read_reg(0x77, (reg_addr + 1))) + local temp = tH*256 + tL + if (temp > 32767) and (signed == true) then + temp = temp - 65536 + end + return temp + end + + -- initialize i2c + --parameters: + --d: sda + --l: scl + function M.init(d, l) + if (d ~= nil) and (l ~= nil) and (d >= 0) and (d <= 11) and (l >= 0) and ( l <= 11) and (d ~= l) then + sda = d + scl = l + else + print("iic config failed!") return nil + end + print("init done") + i2c.setup(id, sda, scl, i2c.SLOW) + --get calibration coefficients. + CO.AC1 = getShort(0xAA, true) + CO.AC2 = getShort(0xAC, true) + CO.AC3 = getShort(0xAE, true) + CO.AC4 = getShort(0xB0) + CO.AC5 = getShort(0xB2) + CO.AC6 = getShort(0xB4) + CO.B1 = getShort(0xB6, true) + CO.B2 = getShort(0xB8, true) + CO.MB = getShort(0xBA, true) + CO.MC = getShort(0xBC, true) + CO.MD = getShort(0xBE, true) + end + + --get temperature from bmp085 + --parameters: + --num_10x: bool value, if true, return number of 0.1 centi-degree + -- default value is false, which return a string , eg: 16.7 + function M.getUT(num_10x) + write_reg(0x77, 0xF4, 0x2E); + tmr.delay(10000); + local temp = getShort(0xF6) + local X1 = (temp - CO.AC6) * CO.AC5 / 32768 + local X2 = CO.MC * 2048/(X1 + CO.MD) + local r = (X2 + X1 + 8)/16 + if(num_10x == true) then + return r + else + return ((r/10).."."..(r%10)) + end + end + + --get raw data of pressure from bmp085 + --parameters: + --oss: over sampling setting, which is 0,1,2,3. Default value is 0 + function M.getUP_raw(oss) + local os = 0 + if ((oss == 0) or (oss == 1) or (oss == 2) or (oss == 3)) and (oss ~= nil) then + os = oss + end + local ov = os * 64 + write_reg(0x77, 0xF4, (0x34 + ov)); + tmr.delay(30000); + --delay 30ms, according to bmp085 document, wait time are: + -- 4.5ms 7.5ms 13.5ms 25.5ms respectively according to oss 0,1,2,3 + local MSB = string.byte(read_reg(0x77, 0xF6)) + local LSB = string.byte(read_reg(0x77, 0xF7)) + local XLSB = string.byte(read_reg(0x77, 0xF8)) + local up_raw = (MSB*65536 + LSB *256 + XLSB)/2^(8 - os) + return up_raw + end + + --get calibrated data of pressure from bmp085 + --parameters: + --oss: over sampling setting, which is 0,1,2,3. Default value is 0 + function M.getUP(oss) + local os = 0 + if ((oss == 0) or (oss == 1) or (oss == 2) or (oss == 3)) and (oss ~= nil) then + os = oss + end + local raw = M.getUP_raw(os) + local B5 = M.getUT(true) * 16 - 8; + local B6 = B5 - 4000 + local X1 = CO.B2 * (B6 * B6 /4096)/2048 + local X2 = CO.AC2 * B6 / 2048 + local X3 = X1 + X2 + local B3 = ((CO.AC1*4 + X3)*2^os + 2)/4 + X1 = CO.AC3 * B6 /8192 + X2 = (CO.B1 * (B6 * B6 / 4096))/65536 + X3 = (X1 + X2 + 2)/4 + local B4 = CO.AC4 * (X3 + 32768) / 32768 + local B7 = (raw -B3) * (50000/2^os) + local p = B7/B4 * 2 + X1 = (p/256)^2 + X1 = (X1 *3038)/65536 + X2 = (-7357 *p)/65536 + p = p +(X1 + X2 + 3791)/16 + return p + end + + --get estimated data of altitude from bmp085 + --parameters: + --oss: over sampling setting, which is 0,1,2,3. Default value is 0 + function M.getAL(oss) + --Altitudi can be calculated by pressure refer to sea level pressure, which is 101325 + --pressure changes 100pa corresponds to 8.43m at sea level + return (M.getUP(oss) - 101325)*843/10000 + end + return M \ No newline at end of file diff --git a/modules/ds18b20/ds18b20.CN.md b/lua modules/ds18b20/ds18b20.CN.md similarity index 100% rename from modules/ds18b20/ds18b20.CN.md rename to lua modules/ds18b20/ds18b20.CN.md diff --git a/modules/ds18b20/ds18b20.EN.md b/lua modules/ds18b20/ds18b20.EN.md similarity index 100% rename from modules/ds18b20/ds18b20.EN.md rename to lua modules/ds18b20/ds18b20.EN.md diff --git a/modules/ds18b20/ds18b20.lua b/lua modules/ds18b20/ds18b20.lua similarity index 100% rename from modules/ds18b20/ds18b20.lua rename to lua modules/ds18b20/ds18b20.lua diff --git a/0.9.2/1M-flash/README.md b/prebuild0.9.2/1M-flash/README.md similarity index 100% rename from 0.9.2/1M-flash/README.md rename to prebuild0.9.2/1M-flash/README.md diff --git a/0.9.2/1M-flash/nodemcu_1M_20141219.bin b/prebuild0.9.2/1M-flash/nodemcu_1M_20141219.bin similarity index 100% rename from 0.9.2/1M-flash/nodemcu_1M_20141219.bin rename to prebuild0.9.2/1M-flash/nodemcu_1M_20141219.bin diff --git a/0.9.2/2M-flash/README.md b/prebuild0.9.2/2M-flash/README.md similarity index 100% rename from 0.9.2/2M-flash/README.md rename to prebuild0.9.2/2M-flash/README.md diff --git a/0.9.2/2M-flash/blank.bin b/prebuild0.9.2/2M-flash/blank.bin similarity index 100% rename from 0.9.2/2M-flash/blank.bin rename to prebuild0.9.2/2M-flash/blank.bin diff --git a/0.9.2/2M-flash/eagle.app.v6.flash.bin b/prebuild0.9.2/2M-flash/eagle.app.v6.flash.bin similarity index 100% rename from 0.9.2/2M-flash/eagle.app.v6.flash.bin rename to prebuild0.9.2/2M-flash/eagle.app.v6.flash.bin diff --git a/0.9.2/2M-flash/eagle.app.v6.irom0text.bin b/prebuild0.9.2/2M-flash/eagle.app.v6.irom0text.bin similarity index 100% rename from 0.9.2/2M-flash/eagle.app.v6.irom0text.bin rename to prebuild0.9.2/2M-flash/eagle.app.v6.irom0text.bin diff --git a/0.9.2/2M-flash/esp_init_data_default.bin b/prebuild0.9.2/2M-flash/esp_init_data_default.bin similarity index 100% rename from 0.9.2/2M-flash/esp_init_data_default.bin rename to prebuild0.9.2/2M-flash/esp_init_data_default.bin diff --git a/0.9.2/2M-flash/nodemcu_2M_20141219.bin b/prebuild0.9.2/2M-flash/nodemcu_2M_20141219.bin similarity index 100% rename from 0.9.2/2M-flash/nodemcu_2M_20141219.bin rename to prebuild0.9.2/2M-flash/nodemcu_2M_20141219.bin diff --git a/0.9.2/4M-flash/README.md b/prebuild0.9.2/4M-flash/README.md similarity index 100% rename from 0.9.2/4M-flash/README.md rename to prebuild0.9.2/4M-flash/README.md diff --git a/0.9.2/4M-flash/blank.bin b/prebuild0.9.2/4M-flash/blank.bin similarity index 100% rename from 0.9.2/4M-flash/blank.bin rename to prebuild0.9.2/4M-flash/blank.bin diff --git a/0.9.2/4M-flash/eagle.app.v6.flash.bin b/prebuild0.9.2/4M-flash/eagle.app.v6.flash.bin similarity index 100% rename from 0.9.2/4M-flash/eagle.app.v6.flash.bin rename to prebuild0.9.2/4M-flash/eagle.app.v6.flash.bin diff --git a/0.9.2/4M-flash/eagle.app.v6.irom0text.bin b/prebuild0.9.2/4M-flash/eagle.app.v6.irom0text.bin similarity index 100% rename from 0.9.2/4M-flash/eagle.app.v6.irom0text.bin rename to prebuild0.9.2/4M-flash/eagle.app.v6.irom0text.bin diff --git a/0.9.2/4M-flash/esp_init_data_default.bin b/prebuild0.9.2/4M-flash/esp_init_data_default.bin similarity index 100% rename from 0.9.2/4M-flash/esp_init_data_default.bin rename to prebuild0.9.2/4M-flash/esp_init_data_default.bin diff --git a/0.9.2/512k-flash/README.md b/prebuild0.9.2/512k-flash/README.md similarity index 100% rename from 0.9.2/512k-flash/README.md rename to prebuild0.9.2/512k-flash/README.md diff --git a/blank512k.bin b/prebuild0.9.2/512k-flash/blank512k.bin similarity index 100% rename from blank512k.bin rename to prebuild0.9.2/512k-flash/blank512k.bin diff --git a/0.9.2/512k-flash/nodemcu_512k_20141212.bin b/prebuild0.9.2/512k-flash/nodemcu_512k_20141212.bin similarity index 100% rename from 0.9.2/512k-flash/nodemcu_512k_20141212.bin rename to prebuild0.9.2/512k-flash/nodemcu_512k_20141212.bin diff --git a/0.9.2/512k-flash/nodemcu_512k_20141219.bin b/prebuild0.9.2/512k-flash/nodemcu_512k_20141219.bin similarity index 100% rename from 0.9.2/512k-flash/nodemcu_512k_20141219.bin rename to prebuild0.9.2/512k-flash/nodemcu_512k_20141219.bin diff --git a/tools/gen_appbin.py b/tools/gen_appbin.py new file mode 100644 index 00000000..66019d15 --- /dev/null +++ b/tools/gen_appbin.py @@ -0,0 +1,71 @@ +#!/usr/bin/python +# +# Copyright (c) 2010 Espressif System +# +# grab user_start() address and pass it to genflashbin program +# + +import string +import sys +import os +import re + +if len(sys.argv) != 3: + print 'Usage: gen_appbin.py eagle.app.out version' + sys.exit(0) + +elf_file = sys.argv[1] +ver = sys.argv[2] +#print elf_file + +cmd = 'xt-nm -g ' + elf_file + ' > eagle.app.sym' +#print cmd +os.system(cmd) + +fp = file('./eagle.app.sym') +if fp is None: + print "open sym file error\n" + exit + +lines = fp.readlines() + +fp.close() + +entry_addr = None +p = re.compile('(\w*)(\sT\s)(call_user_start)$') +for line in lines: + m = p.search(line) + if m != None: + entry_addr = m.group(1) + #entry_addr = int(entry_addr, 16) + print entry_addr + +if entry_addr is None: + print 'no entry point!!' + exit + +data_start_addr = '0' +p = re.compile('(\w*)(\sA\s)(_data_start)$') +for line in lines: + m = p.search(line) + if m != None: + data_start_addr = m.group(1) + print data_start_addr + +rodata_start_addr = '0' +p = re.compile('(\w*)(\sA\s)(_rodata_start)$') +for line in lines: + m = p.search(line) + if m != None: + rodata_start_addr = m.group(1) + print rodata_start_addr + +cmd = 'genflashbin%s eagle.app.%s.text.bin '%(ver, ver)+entry_addr+' eagle.app.%s.data.bin '%(ver)+ data_start_addr+' eagle.app.%s.rodata.bin '%(ver)+rodata_start_addr + +print cmd +os.system(cmd) + +cmd = 'mv eagle.app.flash.bin eagle.app.%s.flash.bin'%(ver) + +print cmd +os.system(cmd) diff --git a/tools/gen_flashbin.py b/tools/gen_flashbin.py new file mode 100644 index 00000000..0f8f92ba --- /dev/null +++ b/tools/gen_flashbin.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +import os +import sys +import binascii +import string + +if len(sys.argv) != 3: + print 'Usage: gen_flashbin.py 1.bin 2.bin' + sys.exit(0) + +bin1_name = sys.argv[1] +bin2_name = sys.argv[2] + +bin1_file = open(bin1_name, 'rb') +bin2_file = open(bin2_name, 'rb') + +bin1_len = os.path.getsize(bin1_name) + +bin1 = bin1_file.read() +bin2 = bin2_file.read() + +bitlist = ['FF']*(0x10000-bin1_len) +bytes = binascii.a2b_hex(''.join(bitlist)) + +bitout = open('eagle.app.flash.bin', 'wb') +bitout.write(bin1) +bitout.write(bytes) +bitout.write(bin2) +bitout.close() + +bin1_file.close() +bin2_file.close() + diff --git a/tools/genflashbinv6.exe b/tools/genflashbinv6.exe new file mode 100644 index 0000000000000000000000000000000000000000..0e1e1d36e6a4f7e674c21d3d5b017624cb669acd GIT binary patch literal 13824 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 s&3|C?=hC%F81T7-P4g|-oP={*=jb1> cert.h +# set default key for use in the server +xxd -i server.key_1024 | sed -e \ + "s/server_key_1024/default_private_key/" > private_key.h diff --git a/tools/xxd.exe b/tools/xxd.exe new file mode 100644 index 0000000000000000000000000000000000000000..d102b464d73958c80d1ddf9b1160ebdcdd7d413d GIT binary patch literal 70144 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